Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 671be0af0a | |||
| 7e9278f992 | |||
| 9194441c47 | |||
| 4f307c0085 | |||
| 3bdda55102 | |||
| b657b0fb1f | |||
| 4935ae6da5 | |||
| 72a50eae43 | |||
| fa37b90cff | |||
| 052a8db606 | |||
| 88ccba166e | |||
| 58998089a6 | |||
| f0a73b329e | |||
| c97678a477 | |||
| 2ad739275f | |||
| caad78885d | |||
| a234227b9f | |||
| 16f736e773 | |||
| 1fe24ad11d | |||
| 33c5e0827a | |||
| cbd36aeb8f | |||
| 2963bb3f41 | |||
| 09a6ceac63 | |||
| 75d8a01cab | |||
| 0968f75e9a | |||
| bc0db88afa | |||
| 5f11f12d0c | |||
| 2d18950542 | |||
| 50d314445a | |||
| f8d57d9265 | |||
| ebbe6db378 | |||
| 543cad3a23 | |||
| 41cc83dbf6 | |||
| ce7101be88 | |||
| 0a241e3a9c | |||
| 37857e88ea | |||
| 6fbcf46a8b | |||
| a36bc8f29a | |||
| 813b7bca6e | |||
| e2945f2a42 | |||
| eaf623a560 | |||
| 26aa504590 | |||
| e4dde3f4b6 | |||
| 9c90699c40 | |||
| b67cd50e8a | |||
| d2acaa03f5 | |||
| f2aa4cd10b | |||
| b940b99e75 | |||
| 868e0706ea | |||
| 324f544ba2 | |||
| 0a1981f90e | |||
| 0a36bfb088 | |||
| 0d94af099b | |||
| 4a6115ed64 | |||
| 42eb319751 | |||
| 343bddc6ad | |||
| 632a742ea9 | |||
| 08c9d1228a | |||
| 11d8b28fb4 | |||
| 12b6371209 | |||
| 24d22aaafb | |||
| ebf7822628 | |||
| 7669fa1582 | |||
| e49ba61917 | |||
| 96db1c2479 | |||
| 7abdb41a9e | |||
| 14f6d2f3b0 | |||
| 4e4b4975ad | |||
| 93fafcf886 | |||
| 82cc88a1b9 | |||
| 5c94e0bccc |
+10
@@ -3,10 +3,20 @@ jdk:
|
||||
- oraclejdk7
|
||||
- openjdk7
|
||||
- openjdk6
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
|
||||
script:
|
||||
- TERM=dumb ./gradlew clean build dist
|
||||
|
||||
after_success:
|
||||
- TERM=dumb ./gradlew jacocoTestReport coveralls
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- skylot@gmail.com
|
||||
|
||||
@@ -105,6 +105,41 @@ as published by the Free Software Foundation.
|
||||
*******************************************************************************
|
||||
|
||||
|
||||
ASM library:
|
||||
|
||||
*******************************************************************************
|
||||
Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holders 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.
|
||||
*******************************************************************************
|
||||
|
||||
|
||||
|
||||
Jadx-gui components
|
||||
===================
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
## JADX
|
||||
## JADX
|
||||
|
||||
[](https://travis-ci.org/skylot/jadx)
|
||||
[](https://drone.io/github.com/skylot/jadx/latest)
|
||||
[](https://coveralls.io/r/skylot/jadx)
|
||||
[](https://scan.coverity.com/projects/2166)
|
||||
|
||||
**jadx** - Dex to Java decompiler
|
||||
|
||||
Command line and GUI tools for produce Java source code from Android Dex and Apk files
|
||||
|
||||
Note: jadx-gui now in experimental stage
|
||||
|
||||

|
||||
|
||||
### Downloads
|
||||
- [unstable](https://drone.io/github.com/skylot/jadx/files)
|
||||
[](https://drone.io/github.com/skylot/jadx/latest)
|
||||
[](https://travis-ci.org/skylot/jadx)
|
||||
- from [github](https://github.com/skylot/jadx/releases)
|
||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
||||
- from [sourceforge](http://sourceforge.net/projects/jadx/files/)
|
||||
|
||||
|
||||
### Building from source
|
||||
### Building from source
|
||||
git clone https://github.com/skylot/jadx.git
|
||||
cd jadx
|
||||
./gradlew dist
|
||||
|
||||
|
||||
(on Windows, use `gradlew.bat` instead of `./gradlew`)
|
||||
|
||||
Scripts for run jadx will be placed in `build/jadx/bin`
|
||||
@@ -36,7 +39,7 @@ Run **jadx** on itself:
|
||||
|
||||
### Usage
|
||||
```
|
||||
jadx[-gui] [options] <input file> (.dex, .apk or .jar)
|
||||
jadx[-gui] [options] <input file> (.dex, .apk, .jar or .class)
|
||||
options:
|
||||
-d, --output-dir - output directory
|
||||
-j, --threads-count - processing threads count
|
||||
@@ -49,6 +52,16 @@ Example:
|
||||
jadx -d out classes.dex
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
##### Out of memory error:
|
||||
- Reduce processing threads count (`-j` option)
|
||||
- Increase maximum java heap size:
|
||||
* command line (example for linux):
|
||||
`JAVA_OPTS="-Xmx4G" jadx -j 1 some.apk`
|
||||
* edit 'jadx' script (jadx.bat on Windows) and setup bigger heap size:
|
||||
`DEFAULT_JVM_OPTS="-Xmx2500M"`
|
||||
|
||||
---------------------------------------
|
||||
*Licensed under the Apache 2.0 License*
|
||||
|
||||
*Copyright 2013 by Skylot*
|
||||
*Copyright 2014 by Skylot*
|
||||
|
||||
+32
-13
@@ -5,20 +5,18 @@ apply plugin: 'sonar-runner'
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'groovy'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
sourceCompatibility = 1.6
|
||||
targetCompatibility = 1.6
|
||||
apply plugin: 'coveralls'
|
||||
|
||||
version = jadxVersion
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
tasks.withType(Compile) {
|
||||
if (!"${it}".contains(':jadx-samples:')) {
|
||||
options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation'
|
||||
}
|
||||
tasks.withType(JavaCompile) {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_6
|
||||
targetCompatibility = JavaVersion.VERSION_1_6
|
||||
|
||||
if (!"$it".contains(':jadx-samples:')) {
|
||||
options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +28,36 @@ subprojects {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.slf4j:slf4j-api:1.7.6'
|
||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||
|
||||
testCompile 'ch.qos.logback:logback-classic:1.1.2'
|
||||
testCompile 'junit:junit:4.11'
|
||||
testCompile 'org.mockito:mockito-core:1.9.5'
|
||||
testCompile 'ch.qos.logback:logback-classic:1.1.1'
|
||||
testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
|
||||
testCompile 'cglib:cglib-nodep:3.1'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled = true // coveralls plugin depends on xml format report
|
||||
html.enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// setup coveralls (http://coveralls.io/) see http://github.com/kt3k/coveralls-gradle-plugin
|
||||
classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:0.6.1'
|
||||
}
|
||||
}
|
||||
|
||||
task copyArtifacts(type: Sync, dependsOn: ['jadx-cli:installApp', 'jadx-gui:installApp']) {
|
||||
@@ -69,5 +88,5 @@ task clean(type: Delete) {
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '1.11'
|
||||
gradleVersion = '2.0'
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-bin.zip
|
||||
|
||||
+2
-12
@@ -5,18 +5,8 @@ applicationName = 'jadx'
|
||||
|
||||
dependencies {
|
||||
compile(project(':jadx-core'))
|
||||
compile 'com.beust:jcommander:1.30'
|
||||
compile 'ch.qos.logback:logback-classic:1.1.1'
|
||||
}
|
||||
|
||||
startScripts {
|
||||
doLast {
|
||||
// increase default max heap size
|
||||
String var = 'DEFAULT_JVM_OPTS='
|
||||
String args = '-Xmx1300M'
|
||||
unixScript.text = unixScript.text.replace(var + '""', var + '"' + args + '"')
|
||||
windowsScript.text = windowsScript.text.replace(var, var + args)
|
||||
}
|
||||
compile 'com.beust:jcommander:1.35'
|
||||
compile 'ch.qos.logback:logback-classic:1.1.2'
|
||||
}
|
||||
|
||||
applicationDistribution.with {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package jadx.cli;
|
||||
|
||||
import jadx.api.Decompiler;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.io.File;
|
||||
@@ -12,39 +11,39 @@ import org.slf4j.LoggerFactory;
|
||||
public class JadxCLI {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws JadxException {
|
||||
try {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs(args);
|
||||
checkArgs(jadxArgs);
|
||||
processAndSave(jadxArgs);
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage());
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (processArgs(jadxArgs, args)) {
|
||||
processAndSave(jadxArgs);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.error("jadx error: " + e.getMessage(), e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processAndSave(JadxCLIArgs jadxArgs) {
|
||||
try {
|
||||
Decompiler jadx = new Decompiler(jadxArgs);
|
||||
jadx.loadFiles(jadxArgs.getInput());
|
||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
||||
jadx.save();
|
||||
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
|
||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
||||
jadx.loadFiles(jadxArgs.getInput());
|
||||
jadx.save();
|
||||
if (jadx.getErrorsCount() != 0) {
|
||||
jadx.printErrorsReport();
|
||||
LOG.error("finished with errors");
|
||||
} else {
|
||||
LOG.info("done");
|
||||
} catch (Throwable e) {
|
||||
LOG.error("jadx error:", e);
|
||||
}
|
||||
int errorsCount = ErrorsCounter.getErrorCount();
|
||||
if (errorsCount != 0) {
|
||||
ErrorsCounter.printReport();
|
||||
}
|
||||
System.exit(errorsCount);
|
||||
}
|
||||
|
||||
private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
static boolean processArgs(JadxCLIArgs jadxArgs, String[] args) throws JadxException {
|
||||
if (!jadxArgs.processArgs(args)) {
|
||||
return false;
|
||||
}
|
||||
if (jadxArgs.getInput().isEmpty()) {
|
||||
LOG.error("Please specify input file");
|
||||
jadxArgs.printUsage();
|
||||
System.exit(1);
|
||||
return false;
|
||||
}
|
||||
File outputDir = jadxArgs.getOutDir();
|
||||
if (outputDir == null) {
|
||||
@@ -64,5 +63,6 @@ public class JadxCLI {
|
||||
if (outputDir.exists() && !outputDir.isDirectory()) {
|
||||
throw new JadxException("Output directory exists as file " + outputDir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package jadx.cli;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.Consts;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
import java.io.File;
|
||||
@@ -20,7 +20,7 @@ import com.beust.jcommander.ParameterException;
|
||||
|
||||
public final class JadxCLIArgs implements IJadxArgs {
|
||||
|
||||
@Parameter(description = "<input file> (.dex, .apk or .jar)")
|
||||
@Parameter(description = "<input file> (.dex, .apk, .jar or .class)")
|
||||
protected List<String> files;
|
||||
|
||||
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
|
||||
@@ -47,25 +47,25 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
private final List<File> input = new ArrayList<File>(1);
|
||||
private File outputDir;
|
||||
|
||||
public JadxCLIArgs(String[] args) {
|
||||
parse(args);
|
||||
processArgs();
|
||||
public boolean processArgs(String[] args) {
|
||||
return parse(args) && process();
|
||||
}
|
||||
|
||||
private void parse(String[] args) {
|
||||
private boolean parse(String[] args) {
|
||||
try {
|
||||
new JCommander(this, args);
|
||||
return true;
|
||||
} catch (ParameterException e) {
|
||||
System.err.println("Arguments parse error: " + e.getMessage());
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void processArgs() {
|
||||
private boolean process() {
|
||||
if (isPrintHelp()) {
|
||||
printUsage();
|
||||
System.exit(0);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (threadsCount <= 0) {
|
||||
@@ -95,8 +95,9 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
} catch (JadxException e) {
|
||||
System.err.println("ERROR: " + e.getMessage());
|
||||
printUsage();
|
||||
System.exit(1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void printUsage() {
|
||||
@@ -104,7 +105,7 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
// print usage in not sorted fields order (by default its sorted by description)
|
||||
PrintStream out = System.out;
|
||||
out.println();
|
||||
out.println("jadx - dex to java decompiler, version: " + Consts.JADX_VERSION);
|
||||
out.println("jadx - dex to java decompiler, version: " + JadxDecompiler.getVersion());
|
||||
out.println();
|
||||
out.println("usage: jadx [options] " + jc.getMainParameterDescription());
|
||||
out.println("options:");
|
||||
@@ -147,6 +148,7 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDir() {
|
||||
return outputDir;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
ext.jadxClasspath = 'clsp-data/android-4.3.jar'
|
||||
|
||||
dependencies {
|
||||
compile 'com.google.android.tools:dx:1.7'
|
||||
|
||||
compile files('lib/dx-1.8.jar')
|
||||
compile 'org.ow2.asm:asm:5.0.3'
|
||||
runtime files(jadxClasspath)
|
||||
}
|
||||
|
||||
task packTests(type: Jar) {
|
||||
classifier = 'tests'
|
||||
from sourceSets.test.output
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -30,6 +30,10 @@ public final class CodePosition {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public boolean isSet() {
|
||||
return line != 0 || offset != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class DefaultJadxArgs implements IJadxArgs {
|
||||
|
||||
@Override
|
||||
public File getOutDir() {
|
||||
return new File("jadx-output");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadsCount() {
|
||||
return Runtime.getRuntime().availableProcessors();
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface IJadxArgs {
|
||||
File getOutDir();
|
||||
|
||||
int getThreadsCount();
|
||||
|
||||
boolean isCFGOutput();
|
||||
|
||||
+60
-34
@@ -7,8 +7,8 @@ import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -31,7 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* Jadx API usage example:
|
||||
* <pre><code>
|
||||
* Decompiler jadx = new Decompiler();
|
||||
* JadxDecompiler jadx = new JadxDecompiler();
|
||||
* jadx.loadFile(new File("classes.dex"));
|
||||
* jadx.setOutputDir(new File("out"));
|
||||
* jadx.save();
|
||||
@@ -44,8 +43,8 @@ import org.slf4j.LoggerFactory;
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
public final class Decompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class);
|
||||
public final class JadxDecompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
|
||||
@@ -56,13 +55,14 @@ public final class Decompiler {
|
||||
private List<IDexTreeVisitor> passes;
|
||||
private List<JavaClass> classes;
|
||||
|
||||
public Decompiler() {
|
||||
this.args = new DefaultJadxArgs();
|
||||
init();
|
||||
public JadxDecompiler() {
|
||||
this(new DefaultJadxArgs());
|
||||
}
|
||||
|
||||
public Decompiler(IJadxArgs jadxArgs) {
|
||||
public JadxDecompiler(IJadxArgs jadxArgs) {
|
||||
this.args = jadxArgs;
|
||||
this.outDir = jadxArgs.getOutDir();
|
||||
reset();
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -73,22 +73,36 @@ public final class Decompiler {
|
||||
|
||||
void init() {
|
||||
if (outDir == null) {
|
||||
outDir = new File("jadx-output");
|
||||
outDir = new DefaultJadxArgs().getOutDir();
|
||||
}
|
||||
this.passes = Jadx.getPassesList(args, outDir);
|
||||
}
|
||||
|
||||
public void loadFile(File file) throws IOException, DecodeException {
|
||||
void reset() {
|
||||
ClassInfo.clearCache();
|
||||
classes = null;
|
||||
root = null;
|
||||
}
|
||||
|
||||
public static String getVersion() {
|
||||
return Jadx.getVersion();
|
||||
}
|
||||
|
||||
public void loadFile(File file) throws JadxException {
|
||||
loadFiles(Collections.singletonList(file));
|
||||
}
|
||||
|
||||
public void loadFiles(List<File> files) throws IOException, DecodeException {
|
||||
public void loadFiles(List<File> files) throws JadxException {
|
||||
if (files.isEmpty()) {
|
||||
throw new JadxRuntimeException("Empty file list");
|
||||
throw new JadxException("Empty file list");
|
||||
}
|
||||
inputFiles.clear();
|
||||
for (File file : files) {
|
||||
inputFiles.add(new InputFile(file));
|
||||
try {
|
||||
inputFiles.add(new InputFile(file));
|
||||
} catch (IOException e) {
|
||||
throw new JadxException("Error load file: " + file, e);
|
||||
}
|
||||
}
|
||||
parse();
|
||||
}
|
||||
@@ -99,28 +113,25 @@ public final class Decompiler {
|
||||
ex.shutdown();
|
||||
ex.awaitTermination(1, TimeUnit.DAYS);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Save interrupted", e);
|
||||
throw new JadxRuntimeException("Save interrupted", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ThreadPoolExecutor getSaveExecutor() {
|
||||
public ExecutorService getSaveExecutor() {
|
||||
if (root == null) {
|
||||
throw new JadxRuntimeException("No loaded files");
|
||||
}
|
||||
int threadsCount = args.getThreadsCount();
|
||||
LOG.debug("processing threads count: {}", threadsCount);
|
||||
|
||||
final List<IDexTreeVisitor> passList = new ArrayList<IDexTreeVisitor>(passes);
|
||||
SaveCode savePass = new SaveCode(outDir, args);
|
||||
passList.add(savePass);
|
||||
|
||||
LOG.info("processing ...");
|
||||
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
|
||||
for (final ClassNode cls : root.getClasses(false)) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threadsCount);
|
||||
for (final JavaClass cls : getClasses()) {
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ProcessClass.process(cls, passList);
|
||||
cls.decompile();
|
||||
SaveCode.save(outDir, args, cls.getClassNode());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -128,11 +139,14 @@ public final class Decompiler {
|
||||
}
|
||||
|
||||
public List<JavaClass> getClasses() {
|
||||
if (root == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (classes == null) {
|
||||
List<ClassNode> classNodeList = root.getClasses(false);
|
||||
List<JavaClass> clsList = new ArrayList<JavaClass>(classNodeList.size());
|
||||
for (ClassNode classNode : classNodeList) {
|
||||
clsList.add(new JavaClass(this, classNode));
|
||||
clsList.add(new JavaClass(classNode, this));
|
||||
}
|
||||
classes = Collections.unmodifiableList(clsList);
|
||||
}
|
||||
@@ -140,8 +154,12 @@ public final class Decompiler {
|
||||
}
|
||||
|
||||
public List<JavaPackage> getPackages() {
|
||||
List<JavaClass> classList = getClasses();
|
||||
if (classList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<String, List<JavaClass>> map = new HashMap<String, List<JavaClass>>();
|
||||
for (JavaClass javaClass : getClasses()) {
|
||||
for (JavaClass javaClass : classList) {
|
||||
String pkg = javaClass.getPackage();
|
||||
List<JavaClass> clsList = map.get(pkg);
|
||||
if (clsList == null) {
|
||||
@@ -159,7 +177,7 @@ public final class Decompiler {
|
||||
Collections.sort(pkg.getClasses(), new Comparator<JavaClass>() {
|
||||
@Override
|
||||
public int compare(JavaClass o1, JavaClass o2) {
|
||||
return o1.getShortName().compareTo(o2.getShortName());
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -167,7 +185,17 @@ public final class Decompiler {
|
||||
}
|
||||
|
||||
public int getErrorsCount() {
|
||||
return ErrorsCounter.getErrorCount();
|
||||
if (root == null) {
|
||||
return 0;
|
||||
}
|
||||
return root.getErrorsCounter().getErrorCount();
|
||||
}
|
||||
|
||||
public void printErrorsReport() {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
root.getErrorsCounter().printReport();
|
||||
}
|
||||
|
||||
void parse() throws DecodeException {
|
||||
@@ -177,14 +205,7 @@ public final class Decompiler {
|
||||
root.load(inputFiles);
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
ClassInfo.clearCache();
|
||||
ErrorsCounter.reset();
|
||||
classes = null;
|
||||
}
|
||||
|
||||
void processClass(ClassNode cls) {
|
||||
LOG.info("processing class {} ...", cls);
|
||||
ProcessClass.process(cls, passes);
|
||||
}
|
||||
|
||||
@@ -203,4 +224,9 @@ public final class Decompiler {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "jadx decompiler " + getVersion();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -15,18 +14,29 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class JavaClass {
|
||||
public final class JavaClass implements JavaNode {
|
||||
|
||||
private final Decompiler decompiler;
|
||||
private final JadxDecompiler decompiler;
|
||||
private final ClassNode cls;
|
||||
private final JavaClass parent;
|
||||
|
||||
private List<JavaClass> innerClasses = Collections.emptyList();
|
||||
private List<JavaField> fields = Collections.emptyList();
|
||||
private List<JavaMethod> methods = Collections.emptyList();
|
||||
|
||||
JavaClass(Decompiler decompiler, ClassNode classNode) {
|
||||
JavaClass(ClassNode classNode, JadxDecompiler decompiler) {
|
||||
this.decompiler = decompiler;
|
||||
this.cls = classNode;
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner classes constructor
|
||||
*/
|
||||
JavaClass(ClassNode classNode, JavaClass parent) {
|
||||
this.decompiler = null;
|
||||
this.cls = classNode;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
@@ -35,12 +45,15 @@ public final class JavaClass {
|
||||
decompile();
|
||||
code = cls.getCode();
|
||||
}
|
||||
return code != null ? code.toString() : "error processing class";
|
||||
if (code == null) {
|
||||
return "";
|
||||
}
|
||||
return code.toString();
|
||||
}
|
||||
|
||||
public void decompile() {
|
||||
if (decompiler == null) {
|
||||
throw new JadxRuntimeException("Can't decompile inner class");
|
||||
return;
|
||||
}
|
||||
if (cls.getCode() == null) {
|
||||
decompiler.processClass(cls);
|
||||
@@ -57,8 +70,8 @@ public final class JavaClass {
|
||||
if (inClsCount != 0) {
|
||||
List<JavaClass> list = new ArrayList<JavaClass>(inClsCount);
|
||||
for (ClassNode inner : cls.getInnerClasses()) {
|
||||
if (!inner.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
|
||||
JavaClass javaClass = new JavaClass(null, inner);
|
||||
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
||||
JavaClass javaClass = new JavaClass(inner, this);
|
||||
javaClass.load();
|
||||
list.add(javaClass);
|
||||
}
|
||||
@@ -70,8 +83,8 @@ public final class JavaClass {
|
||||
if (fieldsCount != 0) {
|
||||
List<JavaField> flds = new ArrayList<JavaField>(fieldsCount);
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
if (!f.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
|
||||
flds.add(new JavaField(f));
|
||||
if (!f.contains(AFlag.DONT_GENERATE)) {
|
||||
flds.add(new JavaField(f, this));
|
||||
}
|
||||
}
|
||||
this.fields = Collections.unmodifiableList(flds);
|
||||
@@ -81,7 +94,7 @@ public final class JavaClass {
|
||||
if (methodsCount != 0) {
|
||||
List<JavaMethod> mths = new ArrayList<JavaMethod>(methodsCount);
|
||||
for (MethodNode m : cls.getMethods()) {
|
||||
if (!m.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
|
||||
if (!m.contains(AFlag.DONT_GENERATE)) {
|
||||
mths.add(new JavaMethod(this, m));
|
||||
}
|
||||
}
|
||||
@@ -127,31 +140,46 @@ public final class JavaClass {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getSourceLine(int decompiledLine) {
|
||||
decompile();
|
||||
return cls.getCode().getLineMapping().get(decompiledLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return cls.getShortName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return cls.getFullName();
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return cls.getShortName();
|
||||
}
|
||||
|
||||
public String getPackage() {
|
||||
return cls.getPackage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public AccessInfo getAccessInfo() {
|
||||
return cls.getAccessFlags();
|
||||
}
|
||||
|
||||
public List<JavaClass> getInnerClasses() {
|
||||
decompile();
|
||||
return innerClasses;
|
||||
}
|
||||
|
||||
public List<JavaField> getFields() {
|
||||
decompile();
|
||||
return fields;
|
||||
}
|
||||
|
||||
public List<JavaMethod> getMethods() {
|
||||
decompile();
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,31 @@ import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
|
||||
public final class JavaField {
|
||||
public final class JavaField implements JavaNode {
|
||||
|
||||
private final FieldNode field;
|
||||
private final JavaClass parent;
|
||||
|
||||
public JavaField(FieldNode f) {
|
||||
JavaField(FieldNode f, JavaClass cls) {
|
||||
this.field = f;
|
||||
this.parent = cls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return field.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return parent.getFullName() + "." + field.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public AccessInfo getAccessFlags() {
|
||||
return field.getAccessFlags();
|
||||
}
|
||||
|
||||
@@ -6,19 +6,26 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class JavaMethod {
|
||||
public final class JavaMethod implements JavaNode {
|
||||
private final MethodNode mth;
|
||||
private final JavaClass parent;
|
||||
|
||||
public JavaMethod(JavaClass cls, MethodNode m) {
|
||||
JavaMethod(JavaClass cls, MethodNode m) {
|
||||
this.parent = cls;
|
||||
this.mth = m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return mth.getMethodInfo().getName();
|
||||
return mth.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return mth.getMethodInfo().getFullName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package jadx.api;
|
||||
|
||||
public interface JavaNode {
|
||||
|
||||
String getName();
|
||||
|
||||
String getFullName();
|
||||
|
||||
JavaClass getDeclaringClass();
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package jadx.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class JavaPackage implements Comparable<JavaPackage> {
|
||||
public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
|
||||
private final String name;
|
||||
private final List<JavaClass> classes;
|
||||
|
||||
@@ -11,14 +11,26 @@ public final class JavaPackage implements Comparable<JavaPackage> {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
// TODO: store full package name
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<JavaClass> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(JavaPackage o) {
|
||||
return name.compareTo(o.name);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package jadx.core;
|
||||
|
||||
public class Consts {
|
||||
public static final String JADX_VERSION = Jadx.getVersion();
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
|
||||
public static final String CLASS_OBJECT = "java.lang.Object";
|
||||
|
||||
@@ -6,6 +6,7 @@ import jadx.core.dex.visitors.BlockMakerVisitor;
|
||||
import jadx.core.dex.visitors.ClassModifier;
|
||||
import jadx.core.dex.visitors.CodeShrinker;
|
||||
import jadx.core.dex.visitors.ConstInlinerVisitor;
|
||||
import jadx.core.dex.visitors.DebugInfoVisitor;
|
||||
import jadx.core.dex.visitors.DotGraphVisitor;
|
||||
import jadx.core.dex.visitors.EnumVisitor;
|
||||
import jadx.core.dex.visitors.FallbackModeVisitor;
|
||||
@@ -13,17 +14,20 @@ import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.MethodInlineVisitor;
|
||||
import jadx.core.dex.visitors.ModVisitor;
|
||||
import jadx.core.dex.visitors.PrepareForCodeGen;
|
||||
import jadx.core.dex.visitors.ReSugarCode;
|
||||
import jadx.core.dex.visitors.SimplifyVisitor;
|
||||
import jadx.core.dex.visitors.regions.CheckRegions;
|
||||
import jadx.core.dex.visitors.regions.IfRegionVisitor;
|
||||
import jadx.core.dex.visitors.regions.ProcessVariables;
|
||||
import jadx.core.dex.visitors.regions.RegionMakerVisitor;
|
||||
import jadx.core.dex.visitors.typeresolver.FinishTypeResolver;
|
||||
import jadx.core.dex.visitors.typeresolver.TypeResolver;
|
||||
import jadx.core.dex.visitors.regions.ReturnVisitor;
|
||||
import jadx.core.dex.visitors.ssa.EliminatePhiNodes;
|
||||
import jadx.core.dex.visitors.ssa.SSATransform;
|
||||
import jadx.core.dex.visitors.typeinference.FinishTypeInference;
|
||||
import jadx.core.dex.visitors.typeinference.TypeInference;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
@@ -51,26 +55,29 @@ public class Jadx {
|
||||
passes.add(new FallbackModeVisitor());
|
||||
} else {
|
||||
passes.add(new BlockMakerVisitor());
|
||||
|
||||
passes.add(new TypeResolver());
|
||||
|
||||
passes.add(new SSATransform());
|
||||
passes.add(new DebugInfoVisitor());
|
||||
passes.add(new TypeInference());
|
||||
if (args.isRawCFGOutput()) {
|
||||
passes.add(new DotGraphVisitor(outDir, false, true));
|
||||
}
|
||||
|
||||
passes.add(new ConstInlinerVisitor());
|
||||
passes.add(new FinishTypeResolver());
|
||||
passes.add(new FinishTypeInference());
|
||||
passes.add(new EliminatePhiNodes());
|
||||
|
||||
passes.add(new ModVisitor());
|
||||
passes.add(new EnumVisitor());
|
||||
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new ReSugarCode());
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(new DotGraphVisitor(outDir, false));
|
||||
}
|
||||
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new RegionMakerVisitor());
|
||||
passes.add(new IfRegionVisitor());
|
||||
passes.add(new ReturnVisitor());
|
||||
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new SimplifyVisitor());
|
||||
@@ -91,15 +98,18 @@ public class Jadx {
|
||||
|
||||
public static String getVersion() {
|
||||
try {
|
||||
Enumeration<URL> resources = Utils.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
|
||||
while (resources.hasMoreElements()) {
|
||||
Manifest manifest = new Manifest(resources.nextElement().openStream());
|
||||
String ver = manifest.getMainAttributes().getValue("jadx-version");
|
||||
if (ver != null) {
|
||||
return ver;
|
||||
ClassLoader classLoader = Utils.class.getClassLoader();
|
||||
if (classLoader != null) {
|
||||
Enumeration<URL> resources = classLoader.getResources("META-INF/MANIFEST.MF");
|
||||
while (resources.hasMoreElements()) {
|
||||
Manifest manifest = new Manifest(resources.nextElement().openStream());
|
||||
String ver = manifest.getMainAttributes().getValue("jadx-version");
|
||||
if (ver != null) {
|
||||
return ver;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
LOG.error("Can't get manifest file", e);
|
||||
}
|
||||
return "dev";
|
||||
|
||||
@@ -3,7 +3,6 @@ package jadx.core;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -22,8 +21,6 @@ public final class ProcessClass {
|
||||
for (IDexTreeVisitor visitor : passes) {
|
||||
DepthTraversal.visit(visitor, cls);
|
||||
}
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Decode exception: " + cls, e);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Class process exception: " + cls, e);
|
||||
} finally {
|
||||
|
||||
@@ -65,6 +65,9 @@ public class ClsSet {
|
||||
for (ClassNode cls : list) {
|
||||
if (cls.getAccessFlags().isPublic()) {
|
||||
NClass nClass = getCls(cls.getRawName(), names);
|
||||
if (nClass == null) {
|
||||
throw new JadxRuntimeException("Missing class: " + cls);
|
||||
}
|
||||
nClass.setParents(makeParentsArray(cls, names));
|
||||
classes[k] = nClass;
|
||||
k++;
|
||||
@@ -102,40 +105,47 @@ public class ClsSet {
|
||||
Utils.makeDirsForFile(output);
|
||||
|
||||
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output));
|
||||
String outputName = output.getName();
|
||||
if (outputName.endsWith(CLST_EXTENSION)) {
|
||||
save(outputStream);
|
||||
} else if (outputName.endsWith(".jar")) {
|
||||
ZipOutputStream out = new ZipOutputStream(outputStream);
|
||||
try {
|
||||
out.putNextEntry(new ZipEntry(CLST_PKG_PATH + "/" + CLST_FILENAME));
|
||||
save(out);
|
||||
out.closeEntry();
|
||||
} finally {
|
||||
out.close();
|
||||
outputStream.close();
|
||||
try {
|
||||
String outputName = output.getName();
|
||||
if (outputName.endsWith(CLST_EXTENSION)) {
|
||||
save(outputStream);
|
||||
} else if (outputName.endsWith(".jar")) {
|
||||
ZipOutputStream out = new ZipOutputStream(outputStream);
|
||||
try {
|
||||
out.putNextEntry(new ZipEntry(CLST_PKG_PATH + "/" + CLST_FILENAME));
|
||||
save(out);
|
||||
out.closeEntry();
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + outputName);
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + outputName);
|
||||
} finally {
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void save(OutputStream output) throws IOException {
|
||||
DataOutputStream out = new DataOutputStream(output);
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
try {
|
||||
out.writeBytes(JADX_CLS_SET_HEADER);
|
||||
out.writeByte(VERSION);
|
||||
|
||||
LOG.info("Classes count: " + classes.length);
|
||||
out.writeInt(classes.length);
|
||||
for (NClass cls : classes) {
|
||||
writeString(out, cls.getName());
|
||||
}
|
||||
for (NClass cls : classes) {
|
||||
NClass[] parents = cls.getParents();
|
||||
out.writeByte(parents.length);
|
||||
for (NClass parent : parents) {
|
||||
out.writeInt(parent.getId());
|
||||
LOG.info("Classes count: " + classes.length);
|
||||
out.writeInt(classes.length);
|
||||
for (NClass cls : classes) {
|
||||
writeString(out, cls.getName());
|
||||
}
|
||||
for (NClass cls : classes) {
|
||||
NClass[] parents = cls.getParents();
|
||||
out.writeByte(parents.length);
|
||||
for (NClass parent : parents) {
|
||||
out.writeInt(parent.getId());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,55 +154,67 @@ public class ClsSet {
|
||||
if (input == null) {
|
||||
throw new JadxRuntimeException("Can't load classpath file: " + CLST_FILENAME);
|
||||
}
|
||||
load(input);
|
||||
try {
|
||||
load(input);
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void load(File input) throws IOException, DecodeException {
|
||||
String name = input.getName();
|
||||
InputStream inputStream = new FileInputStream(input);
|
||||
if (name.endsWith(CLST_EXTENSION)) {
|
||||
load(inputStream);
|
||||
} else if (name.endsWith(".jar")) {
|
||||
ZipInputStream in = new ZipInputStream(inputStream);
|
||||
try {
|
||||
ZipEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.getName().endsWith(CLST_EXTENSION)) {
|
||||
load(in);
|
||||
try {
|
||||
if (name.endsWith(CLST_EXTENSION)) {
|
||||
load(inputStream);
|
||||
} else if (name.endsWith(".jar")) {
|
||||
ZipInputStream in = new ZipInputStream(inputStream);
|
||||
try {
|
||||
ZipEntry entry = in.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.getName().endsWith(CLST_EXTENSION)) {
|
||||
load(in);
|
||||
}
|
||||
entry = in.getNextEntry();
|
||||
}
|
||||
entry = in.getNextEntry();
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + name);
|
||||
}
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unknown file format: " + name);
|
||||
} finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void load(InputStream input) throws IOException, DecodeException {
|
||||
DataInputStream in = new DataInputStream(input);
|
||||
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
|
||||
int readHeaderLength = in.read(header);
|
||||
int version = in.readByte();
|
||||
if (readHeaderLength != JADX_CLS_SET_HEADER.length()
|
||||
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))
|
||||
|| version != VERSION) {
|
||||
throw new DecodeException("Wrong jadx class set header");
|
||||
}
|
||||
int count = in.readInt();
|
||||
classes = new NClass[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = readString(in);
|
||||
classes[i] = new NClass(name, i);
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
int pCount = in.readByte();
|
||||
NClass[] parents = new NClass[pCount];
|
||||
for (int j = 0; j < pCount; j++) {
|
||||
parents[j] = classes[in.readInt()];
|
||||
try {
|
||||
byte[] header = new byte[JADX_CLS_SET_HEADER.length()];
|
||||
int readHeaderLength = in.read(header);
|
||||
int version = in.readByte();
|
||||
if (readHeaderLength != JADX_CLS_SET_HEADER.length()
|
||||
|| !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET))
|
||||
|| version != VERSION) {
|
||||
throw new DecodeException("Wrong jadx class set header");
|
||||
}
|
||||
classes[i].setParents(parents);
|
||||
int count = in.readInt();
|
||||
classes = new NClass[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = readString(in);
|
||||
classes[i] = new NClass(name, i);
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
int pCount = in.readByte();
|
||||
NClass[] parents = new NClass[pCount];
|
||||
for (int j = 0; j < pCount; j++) {
|
||||
parents[j] = classes[in.readInt()];
|
||||
}
|
||||
classes[i].setParents(parents);
|
||||
}
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,15 +77,15 @@ public class ClspGraph {
|
||||
return clsName;
|
||||
}
|
||||
NClass cls = nameMap.get(implClsName);
|
||||
if (cls != null) {
|
||||
if (isImplements(clsName, implClsName)) {
|
||||
return implClsName;
|
||||
}
|
||||
Set<String> anc = getAncestors(clsName);
|
||||
return searchCommonParent(anc, cls);
|
||||
if (cls == null) {
|
||||
LOG.debug("Missing class: {}", implClsName);
|
||||
return null;
|
||||
}
|
||||
LOG.debug("Missing class: {}", implClsName);
|
||||
return null;
|
||||
if (isImplements(clsName, implClsName)) {
|
||||
return implClsName;
|
||||
}
|
||||
Set<String> anc = getAncestors(clsName);
|
||||
return searchCommonParent(anc, cls);
|
||||
}
|
||||
|
||||
private String searchCommonParent(Set<String> anc, NClass cls) {
|
||||
@@ -93,11 +93,10 @@ public class ClspGraph {
|
||||
String name = p.getName();
|
||||
if (anc.contains(name)) {
|
||||
return name;
|
||||
} else {
|
||||
String r = searchCommonParent(anc, p);
|
||||
if (r != null) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
String r = searchCommonParent(anc, p);
|
||||
if (r != null) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -109,17 +108,17 @@ public class ClspGraph {
|
||||
return result;
|
||||
}
|
||||
NClass cls = nameMap.get(clsName);
|
||||
if (cls != null) {
|
||||
result = new HashSet<String>();
|
||||
addAncestorsNames(cls, result);
|
||||
if (result.isEmpty()) {
|
||||
result = Collections.emptySet();
|
||||
}
|
||||
ancestorCache.put(clsName, result);
|
||||
return result;
|
||||
if (cls == null) {
|
||||
LOG.debug("Missing class: {}", clsName);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
LOG.debug("Missing class: {}", clsName);
|
||||
return Collections.emptySet();
|
||||
result = new HashSet<String>();
|
||||
addAncestorsNames(cls, result);
|
||||
if (result.isEmpty()) {
|
||||
result = Collections.emptySet();
|
||||
}
|
||||
ancestorCache.put(clsName, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addAncestorsNames(NClass cls, Set<String> result) {
|
||||
|
||||
@@ -54,6 +54,9 @@ public class ConvertToClsSet {
|
||||
|
||||
private static void addFilesFromDirectory(File dir, List<InputFile> inputFiles) throws IOException, DecodeException {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
addFilesFromDirectory(file, inputFiles);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
@@ -53,7 +53,7 @@ public class AnnotationGen {
|
||||
}
|
||||
|
||||
private void add(IAttributeNode node, CodeWriter code) {
|
||||
AnnotationsList aList = (AnnotationsList) node.getAttributes().get(AttributeType.ANNOTATION_LIST);
|
||||
AnnotationsList aList = node.get(AType.ANNOTATION_LIST);
|
||||
if (aList == null || aList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class AnnotationGen {
|
||||
|
||||
private void formatAnnotation(CodeWriter code, Annotation a) {
|
||||
code.add('@');
|
||||
code.add(classGen.useClass(a.getType()));
|
||||
classGen.useType(code, a.getType());
|
||||
Map<String, Object> vl = a.getValues();
|
||||
if (!vl.isEmpty()) {
|
||||
code.add('(');
|
||||
@@ -96,13 +96,13 @@ public class AnnotationGen {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addThrows(MethodNode mth, CodeWriter code) {
|
||||
Annotation an = mth.getAttributes().getAnnotation(Consts.DALVIK_THROWS);
|
||||
Annotation an = mth.getAnnotation(Consts.DALVIK_THROWS);
|
||||
if (an != null) {
|
||||
Object exs = an.getDefaultValue();
|
||||
code.add(" throws ");
|
||||
for (Iterator<ArgType> it = ((List<ArgType>) exs).iterator(); it.hasNext(); ) {
|
||||
ArgType ex = it.next();
|
||||
code.add(TypeGen.translate(classGen, ex));
|
||||
classGen.useType(code, ex);
|
||||
if (it.hasNext()) {
|
||||
code.add(", ");
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class AnnotationGen {
|
||||
}
|
||||
|
||||
public Object getAnnotationDefaultValue(String name) {
|
||||
Annotation an = cls.getAttributes().getAnnotation(Consts.DALVIK_ANNOTATION_DEFAULT);
|
||||
Annotation an = cls.getAnnotation(Consts.DALVIK_ANNOTATION_DEFAULT);
|
||||
if (an != null) {
|
||||
Annotation defAnnotation = (Annotation) an.getDefaultValue();
|
||||
return defAnnotation.getValues().get(name);
|
||||
@@ -144,11 +144,12 @@ public class AnnotationGen {
|
||||
} else if (val instanceof Byte) {
|
||||
code.add(TypeGen.formatByte((Byte) val));
|
||||
} else if (val instanceof ArgType) {
|
||||
code.add(TypeGen.translate(classGen, (ArgType) val)).add(".class");
|
||||
classGen.useType(code, (ArgType) val);
|
||||
code.add(".class");
|
||||
} else if (val instanceof FieldInfo) {
|
||||
// must be a static field
|
||||
FieldInfo field = (FieldInfo) val;
|
||||
code.add(InsnGen.makeStaticFieldAccess(field, classGen));
|
||||
InsnGen.makeStaticFieldAccess(code, field, classGen);
|
||||
} else if (val instanceof List) {
|
||||
code.add('{');
|
||||
Iterator<?> it = ((List) val).iterator();
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.EnumClassAttr.EnumField;
|
||||
import jadx.core.dex.attributes.SourceFileAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
@@ -43,7 +44,7 @@ public class ClassGen {
|
||||
private final boolean fallback;
|
||||
|
||||
private final Set<ClassInfo> imports = new HashSet<ClassInfo>();
|
||||
private int clsDeclLine = 0;
|
||||
private int clsDeclLine;
|
||||
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen, boolean fallback) {
|
||||
this.cls = cls;
|
||||
@@ -62,12 +63,10 @@ public class ClassGen {
|
||||
addClassCode(clsBody);
|
||||
|
||||
CodeWriter clsCode = new CodeWriter();
|
||||
|
||||
if (!"".equals(cls.getPackage())) {
|
||||
clsCode.add("package ").add(cls.getPackage()).add(';');
|
||||
clsCode.newLine();
|
||||
}
|
||||
|
||||
int importsCount = imports.size();
|
||||
if (importsCount != 0) {
|
||||
List<String> sortImports = new ArrayList<String>(importsCount);
|
||||
@@ -84,16 +83,15 @@ public class ClassGen {
|
||||
sortImports.clear();
|
||||
imports.clear();
|
||||
}
|
||||
|
||||
clsCode.add(clsBody);
|
||||
return clsCode;
|
||||
}
|
||||
|
||||
public void addClassCode(CodeWriter code) throws CodegenException {
|
||||
if (cls.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
|
||||
if (cls.contains(AFlag.DONT_GENERATE)) {
|
||||
return;
|
||||
}
|
||||
if (cls.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)) {
|
||||
if (cls.contains(AFlag.INCONSISTENT_CODE)) {
|
||||
code.startLine("// jadx: inconsistent code");
|
||||
}
|
||||
addClassDeclaration(code);
|
||||
@@ -105,7 +103,9 @@ public class ClassGen {
|
||||
if (af.isInterface()) {
|
||||
af = af.remove(AccessFlags.ACC_ABSTRACT);
|
||||
} else if (af.isEnum()) {
|
||||
af = af.remove(AccessFlags.ACC_FINAL).remove(AccessFlags.ACC_ABSTRACT);
|
||||
af = af.remove(AccessFlags.ACC_FINAL)
|
||||
.remove(AccessFlags.ACC_ABSTRACT)
|
||||
.remove(AccessFlags.ACC_STATIC);
|
||||
}
|
||||
|
||||
annotationGen.addForClass(clsCode);
|
||||
@@ -130,7 +130,9 @@ public class ClassGen {
|
||||
if (sup != null
|
||||
&& !sup.getFullName().equals(Consts.CLASS_OBJECT)
|
||||
&& !sup.getFullName().equals(Consts.CLASS_ENUM)) {
|
||||
clsCode.add("extends ").add(useClass(sup)).add(' ');
|
||||
clsCode.add("extends ");
|
||||
useClass(clsCode, sup);
|
||||
clsCode.add(' ');
|
||||
}
|
||||
|
||||
if (cls.getInterfaces().size() > 0 && !af.isAnnotation()) {
|
||||
@@ -141,7 +143,7 @@ public class ClassGen {
|
||||
}
|
||||
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext(); ) {
|
||||
ClassInfo interf = it.next();
|
||||
clsCode.add(useClass(interf));
|
||||
useClass(clsCode, interf);
|
||||
if (it.hasNext()) {
|
||||
clsCode.add(", ");
|
||||
}
|
||||
@@ -165,12 +167,20 @@ public class ClassGen {
|
||||
if (i != 0) {
|
||||
code.add(", ");
|
||||
}
|
||||
code.add(useClass(type));
|
||||
if (type.isGenericType()) {
|
||||
code.add(type.getObject());
|
||||
} else {
|
||||
useClass(code, ClassInfo.fromType(type));
|
||||
}
|
||||
if (list != null && !list.isEmpty()) {
|
||||
code.add(" extends ");
|
||||
for (Iterator<ArgType> it = list.iterator(); it.hasNext(); ) {
|
||||
ArgType g = it.next();
|
||||
code.add(useClass(g));
|
||||
if (g.isGenericType()) {
|
||||
code.add(g.getObject());
|
||||
} else {
|
||||
useClass(code, ClassInfo.fromType(g));
|
||||
}
|
||||
if (it.hasNext()) {
|
||||
code.add(" & ");
|
||||
}
|
||||
@@ -194,35 +204,56 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
private void addInnerClasses(CodeWriter code, ClassNode cls) throws CodegenException {
|
||||
for (ClassNode innerCls : cls.getInnerClasses()) {
|
||||
if (innerCls.contains(AFlag.DONT_GENERATE)
|
||||
|| innerCls.isAnonymous()) {
|
||||
continue;
|
||||
}
|
||||
ClassGen inClGen = new ClassGen(innerCls, getParentGen(), fallback);
|
||||
code.newLine();
|
||||
inClGen.addClassCode(code);
|
||||
imports.addAll(inClGen.getImports());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInnerClassesPresents() {
|
||||
for (ClassNode innerCls : cls.getInnerClasses()) {
|
||||
if (!innerCls.isAnonymous()) {
|
||||
ClassGen inClGen = new ClassGen(innerCls, getParentGen(), fallback);
|
||||
code.newLine();
|
||||
inClGen.addClassCode(code);
|
||||
imports.addAll(inClGen.getImports());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addMethods(CodeWriter code) {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
if (!mth.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
|
||||
try {
|
||||
if (code.getLine() != clsDeclLine) {
|
||||
code.newLine();
|
||||
}
|
||||
addMethod(code, mth);
|
||||
} catch (Exception e) {
|
||||
String msg = ErrorsCounter.methodError(mth, "Method generation error", e);
|
||||
code.startLine("/* " + msg + CodeWriter.NL + Utils.getStackTrace(e) + " */");
|
||||
}
|
||||
if (mth.contains(AFlag.DONT_GENERATE)) {
|
||||
continue;
|
||||
}
|
||||
if (code.getLine() != clsDeclLine) {
|
||||
code.newLine();
|
||||
}
|
||||
try {
|
||||
addMethod(code, mth);
|
||||
} catch (Exception e) {
|
||||
String msg = ErrorsCounter.methodError(mth, "Method generation error", e);
|
||||
code.startLine("/* " + msg + CodeWriter.NL + Utils.getStackTrace(e) + " */");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMethodsPresents() {
|
||||
for (MethodNode mth : cls.getMethods()) {
|
||||
if (!mth.contains(AFlag.DONT_GENERATE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addMethod(CodeWriter code, MethodNode mth) throws CodegenException {
|
||||
MethodGen mthGen = new MethodGen(this, mth);
|
||||
if (mth.getAccessFlags().isAbstract() || mth.getAccessFlags().isNative()) {
|
||||
MethodGen mthGen = new MethodGen(this, mth);
|
||||
mthGen.addDefinition(code);
|
||||
if (cls.getAccessFlags().isAnnotation()) {
|
||||
Object def = annotationGen.getAnnotationDefaultValue(mth.getName());
|
||||
@@ -233,11 +264,17 @@ public class ClassGen {
|
||||
}
|
||||
code.add(';');
|
||||
} else {
|
||||
boolean badCode = mth.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE);
|
||||
boolean badCode = mth.contains(AFlag.INCONSISTENT_CODE);
|
||||
if (badCode) {
|
||||
code.startLine("/* JADX WARNING: inconsistent code. */");
|
||||
code.startLine("/* Code decompiled incorrectly, please refer to instructions dump. */");
|
||||
LOG.error(ErrorsCounter.formatErrorMsg(mth, " Inconsistent code"));
|
||||
ErrorsCounter.methodError(mth, "Inconsistent code");
|
||||
}
|
||||
MethodGen mthGen;
|
||||
if (badCode || mth.contains(AType.JADX_ERROR)) {
|
||||
mthGen = MethodGen.getFallbackMethodGen(mth);
|
||||
} else {
|
||||
mthGen = new MethodGen(this, mth);
|
||||
}
|
||||
if (mthGen.addDefinition(code)) {
|
||||
code.add(' ');
|
||||
@@ -254,15 +291,15 @@ public class ClassGen {
|
||||
private void addFields(CodeWriter code) throws CodegenException {
|
||||
addEnumFields(code);
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
if (f.getAttributes().contains(AttributeFlag.DONT_GENERATE)) {
|
||||
if (f.contains(AFlag.DONT_GENERATE)) {
|
||||
continue;
|
||||
}
|
||||
annotationGen.addForField(code, f);
|
||||
code.startLine(f.getAccessFlags().makeString());
|
||||
code.add(TypeGen.translate(this, f.getType()));
|
||||
useType(code, f.getType());
|
||||
code.add(' ');
|
||||
code.add(f.getName());
|
||||
FieldValueAttr fv = (FieldValueAttr) f.getAttributes().get(AttributeType.FIELD_VALUE);
|
||||
FieldValueAttr fv = f.get(AType.FIELD_VALUE);
|
||||
if (fv != null) {
|
||||
code.add(" = ");
|
||||
if (fv.getValue() == null) {
|
||||
@@ -276,82 +313,104 @@ public class ClassGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void addEnumFields(CodeWriter code) throws CodegenException {
|
||||
EnumClassAttr enumFields = (EnumClassAttr) cls.getAttributes().get(AttributeType.ENUM_CLASS);
|
||||
if (enumFields != null) {
|
||||
InsnGen igen = null;
|
||||
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
|
||||
EnumField f = it.next();
|
||||
code.startLine(f.getName());
|
||||
if (f.getArgs().size() != 0) {
|
||||
code.add('(');
|
||||
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
|
||||
InsnArg arg = aIt.next();
|
||||
if (igen == null) {
|
||||
// don't init mth gen if this is simple enum
|
||||
MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod());
|
||||
igen = new InsnGen(mthGen, false);
|
||||
}
|
||||
igen.addArg(code, arg);
|
||||
if (aIt.hasNext()) {
|
||||
code.add(", ");
|
||||
}
|
||||
}
|
||||
code.add(')');
|
||||
}
|
||||
if (f.getCls() != null) {
|
||||
new ClassGen(f.getCls(), this, fallback).addClassBody(code);
|
||||
}
|
||||
if (it.hasNext()) {
|
||||
code.add(',');
|
||||
}
|
||||
private boolean isFieldsPresents() {
|
||||
for (FieldNode field : cls.getFields()) {
|
||||
if (!field.contains(AFlag.DONT_GENERATE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addEnumFields(CodeWriter code) throws CodegenException {
|
||||
EnumClassAttr enumFields = cls.get(AType.ENUM_CLASS);
|
||||
if (enumFields == null) {
|
||||
return;
|
||||
}
|
||||
InsnGen igen = null;
|
||||
for (Iterator<EnumField> it = enumFields.getFields().iterator(); it.hasNext(); ) {
|
||||
EnumField f = it.next();
|
||||
code.startLine(f.getName());
|
||||
if (f.getArgs().size() != 0) {
|
||||
code.add('(');
|
||||
for (Iterator<InsnArg> aIt = f.getArgs().iterator(); aIt.hasNext(); ) {
|
||||
InsnArg arg = aIt.next();
|
||||
if (igen == null) {
|
||||
// don't init mth gen if this is simple enum
|
||||
MethodGen mthGen = new MethodGen(this, enumFields.getStaticMethod());
|
||||
igen = new InsnGen(mthGen, false);
|
||||
}
|
||||
igen.addArg(code, arg);
|
||||
if (aIt.hasNext()) {
|
||||
code.add(", ");
|
||||
}
|
||||
}
|
||||
code.add(')');
|
||||
}
|
||||
if (f.getCls() != null) {
|
||||
code.add(' ');
|
||||
new ClassGen(f.getCls(), this, fallback).addClassBody(code);
|
||||
}
|
||||
if (it.hasNext()) {
|
||||
code.add(',');
|
||||
}
|
||||
}
|
||||
if (isMethodsPresents() || isFieldsPresents() || isInnerClassesPresents()) {
|
||||
if (enumFields.getFields().isEmpty()) {
|
||||
code.startLine();
|
||||
}
|
||||
code.add(';');
|
||||
code.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
public String useClass(ArgType clsType) {
|
||||
if (clsType.isGenericType()) {
|
||||
return clsType.getObject();
|
||||
}
|
||||
return useClass(ClassInfo.fromType(clsType));
|
||||
}
|
||||
|
||||
public String useClass(ClassInfo classInfo) {
|
||||
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
|
||||
ArgType type = classInfo.getType();
|
||||
ArgType[] generics = type.getGenericTypes();
|
||||
if (generics == null) {
|
||||
return baseClass;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(baseClass);
|
||||
sb.append('<');
|
||||
int len = generics.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
ArgType gt = generics[i];
|
||||
ArgType wt = gt.getWildcardType();
|
||||
if (wt != null) {
|
||||
sb.append('?');
|
||||
int bounds = gt.getWildcardBounds();
|
||||
if (bounds != 0) {
|
||||
sb.append(bounds == -1 ? " super " : " extends ");
|
||||
sb.append(TypeGen.translate(this, wt));
|
||||
}
|
||||
public void useType(CodeWriter code, ArgType type) {
|
||||
PrimitiveType stype = type.getPrimitiveType();
|
||||
if (stype == null) {
|
||||
code.add(type.toString());
|
||||
} else if (stype == PrimitiveType.OBJECT) {
|
||||
if (type.isGenericType()) {
|
||||
code.add(type.getObject());
|
||||
} else {
|
||||
sb.append(TypeGen.translate(this, gt));
|
||||
useClass(code, ClassInfo.fromType(type));
|
||||
}
|
||||
} else if (stype == PrimitiveType.ARRAY) {
|
||||
useType(code, type.getArrayElement());
|
||||
code.add("[]");
|
||||
} else {
|
||||
code.add(stype.getLongName());
|
||||
}
|
||||
}
|
||||
|
||||
public void useClass(CodeWriter code, ClassInfo classInfo) {
|
||||
ClassNode classNode = cls.dex().resolveClass(classInfo);
|
||||
if (classNode != null) {
|
||||
code.attachAnnotation(classNode);
|
||||
}
|
||||
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
|
||||
ArgType[] generics = classInfo.getType().getGenericTypes();
|
||||
code.add(baseClass);
|
||||
if (generics != null) {
|
||||
code.add('<');
|
||||
int len = generics.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (i != 0) {
|
||||
code.add(", ");
|
||||
}
|
||||
ArgType gt = generics[i];
|
||||
ArgType wt = gt.getWildcardType();
|
||||
if (wt != null) {
|
||||
code.add('?');
|
||||
int bounds = gt.getWildcardBounds();
|
||||
if (bounds != 0) {
|
||||
code.add(bounds == -1 ? " super " : " extends ");
|
||||
useType(code, wt);
|
||||
}
|
||||
} else {
|
||||
useType(code, gt);
|
||||
}
|
||||
}
|
||||
code.add('>');
|
||||
}
|
||||
sb.append('>');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String useClassInternal(ClassInfo useCls, ClassInfo classInfo) {
|
||||
@@ -441,7 +500,7 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
private void insertSourceFileInfo(CodeWriter code, AttrNode node) {
|
||||
SourceFileAttr sourceFileAttr = (SourceFileAttr) node.getAttributes().get(AttributeType.SOURCE_FILE);
|
||||
SourceFileAttr sourceFileAttr = node.get(AType.SOURCE_FILE);
|
||||
if (sourceFileAttr != null) {
|
||||
code.startLine("// compiled from: ").add(sourceFileAttr.getFileName());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.core.dex.attributes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
@@ -10,6 +10,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -37,17 +38,13 @@ public class CodeWriter {
|
||||
private int line = 1;
|
||||
private int offset = 0;
|
||||
private Map<CodePosition, Object> annotations = Collections.emptyMap();
|
||||
private Map<Integer, Integer> lineMap = Collections.emptyMap();
|
||||
|
||||
public CodeWriter() {
|
||||
this.indent = 0;
|
||||
this.indentStr = "";
|
||||
}
|
||||
|
||||
public CodeWriter(int indent) {
|
||||
this.indent = indent;
|
||||
updateIndent();
|
||||
}
|
||||
|
||||
public CodeWriter startLine() {
|
||||
addLine();
|
||||
addLineIndent();
|
||||
@@ -68,11 +65,6 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter add(Object obj) {
|
||||
add(obj.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter add(String str) {
|
||||
buf.append(str);
|
||||
offset += str.length();
|
||||
@@ -91,6 +83,9 @@ public class CodeWriter {
|
||||
CodePosition pos = entry.getKey();
|
||||
attachAnnotation(entry.getValue(), new CodePosition(line + pos.getLine(), pos.getOffset()));
|
||||
}
|
||||
for (Map.Entry<Integer, Integer> entry : code.lineMap.entrySet()) {
|
||||
attachSourceLine(line + entry.getKey(), entry.getValue());
|
||||
}
|
||||
line += code.line;
|
||||
offset = code.offset;
|
||||
buf.append(code);
|
||||
@@ -102,6 +97,11 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter addIndent() {
|
||||
add(INDENT);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addLine() {
|
||||
buf.append(NL);
|
||||
line++;
|
||||
@@ -114,11 +114,6 @@ public class CodeWriter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeWriter addIndent() {
|
||||
add(INDENT);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void updateIndent() {
|
||||
int curIndent = indent;
|
||||
if (curIndent < INDENT_CACHE.length) {
|
||||
@@ -189,6 +184,24 @@ public class CodeWriter {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public void attachSourceLine(int sourceLine) {
|
||||
if (sourceLine == 0) {
|
||||
return;
|
||||
}
|
||||
attachSourceLine(line, sourceLine);
|
||||
}
|
||||
|
||||
private void attachSourceLine(int decompiledLine, int sourceLine) {
|
||||
if (lineMap.isEmpty()) {
|
||||
lineMap = new TreeMap<Integer, Integer>();
|
||||
}
|
||||
lineMap.put(decompiledLine, sourceLine);
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getLineMapping() {
|
||||
return lineMap;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
buf.trimToSize();
|
||||
Iterator<Map.Entry<CodePosition, Object>> it = annotations.entrySet().iterator();
|
||||
@@ -206,9 +219,8 @@ public class CodeWriter {
|
||||
private static String removeFirstEmptyLine(String str) {
|
||||
if (str.startsWith(NL)) {
|
||||
return str.substring(NL.length());
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public int length() {
|
||||
|
||||
@@ -10,6 +10,7 @@ import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.regions.Compare;
|
||||
import jadx.core.dex.regions.IfCondition;
|
||||
import jadx.core.dex.regions.IfCondition.Mode;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
@@ -75,7 +76,7 @@ public class ConditionGen extends InsnGen {
|
||||
} else if (op == IfOp.NE) {
|
||||
// != true
|
||||
code.add('!');
|
||||
boolean wrap = isWrapNeeded(firstArg);
|
||||
boolean wrap = isArgWrapNeeded(firstArg);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
@@ -88,9 +89,9 @@ public class ConditionGen extends InsnGen {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Unsupported boolean condition " + op.getSymbol()));
|
||||
}
|
||||
|
||||
addArg(code, firstArg, isWrapNeeded(firstArg));
|
||||
addArg(code, firstArg, isArgWrapNeeded(firstArg));
|
||||
code.add(' ').add(op.getSymbol()).add(' ');
|
||||
addArg(code, secondArg, isWrapNeeded(secondArg));
|
||||
addArg(code, secondArg, isArgWrapNeeded(secondArg));
|
||||
}
|
||||
|
||||
private void addNot(CodeWriter code, IfCondition condition) throws CodegenException {
|
||||
@@ -99,7 +100,7 @@ public class ConditionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private void addAndOr(CodeWriter code, IfCondition condition) throws CodegenException {
|
||||
String mode = condition.getMode() == IfCondition.Mode.AND ? " && " : " || ";
|
||||
String mode = condition.getMode() == Mode.AND ? " && " : " || ";
|
||||
Iterator<IfCondition> it = condition.getArgs().iterator();
|
||||
while (it.hasNext()) {
|
||||
wrap(code, it.next());
|
||||
@@ -110,15 +111,16 @@ public class ConditionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private boolean isWrapNeeded(IfCondition condition) {
|
||||
return !condition.isCompare();
|
||||
return !condition.isCompare() && condition.getMode() != Mode.NOT;
|
||||
}
|
||||
|
||||
private static boolean isWrapNeeded(InsnArg arg) {
|
||||
private static boolean isArgWrapNeeded(InsnArg arg) {
|
||||
if (!arg.isInsnWrap()) {
|
||||
return false;
|
||||
}
|
||||
InsnNode insn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
if (insn.getType() == InsnType.ARITH) {
|
||||
InsnType insnType = insn.getType();
|
||||
if (insnType == InsnType.ARITH) {
|
||||
switch (((ArithNode) insn).getOp()) {
|
||||
case ADD:
|
||||
case SUB:
|
||||
@@ -127,8 +129,18 @@ public class ConditionGen extends InsnGen {
|
||||
case REM:
|
||||
return false;
|
||||
}
|
||||
} else if (insn.getType() == InsnType.INVOKE) {
|
||||
return false;
|
||||
} else {
|
||||
switch (insnType) {
|
||||
case INVOKE:
|
||||
case SGET:
|
||||
case IGET:
|
||||
case AGET:
|
||||
case CONST:
|
||||
case ARRAY_LENGTH:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.attributes.MethodInlineAttr;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
@@ -59,7 +58,7 @@ public class InsnGen {
|
||||
protected final RootNode root;
|
||||
protected final boolean fallback;
|
||||
|
||||
private static enum Flags {
|
||||
private enum Flags {
|
||||
BODY_ONLY,
|
||||
BODY_ONLY_NOWRAP,
|
||||
}
|
||||
@@ -89,7 +88,7 @@ public class InsnGen {
|
||||
|
||||
public void addArg(CodeWriter code, InsnArg arg, boolean wrap) throws CodegenException {
|
||||
if (arg.isRegister()) {
|
||||
code.add(mgen.makeArgName((RegisterArg) arg));
|
||||
code.add(mgen.getNameGen().useArg((RegisterArg) arg));
|
||||
} else if (arg.isLiteral()) {
|
||||
code.add(lit((LiteralArg) arg));
|
||||
} else if (arg.isInsnWrap()) {
|
||||
@@ -100,11 +99,9 @@ public class InsnGen {
|
||||
} else if (arg.isField()) {
|
||||
FieldArg f = (FieldArg) arg;
|
||||
if (f.isStatic()) {
|
||||
code.add(staticField(f.getField()));
|
||||
staticField(code, f.getField());
|
||||
} else {
|
||||
RegisterArg regArg = new RegisterArg(f.getRegNum());
|
||||
regArg.replaceTypedVar(f);
|
||||
instanceField(code, f.getField(), regArg);
|
||||
instanceField(code, f.getField(), f.getInstanceArg());
|
||||
}
|
||||
} else {
|
||||
throw new CodegenException("Unknown arg type " + arg);
|
||||
@@ -113,7 +110,7 @@ public class InsnGen {
|
||||
|
||||
public void assignVar(CodeWriter code, InsnNode insn) throws CodegenException {
|
||||
RegisterArg arg = insn.getResult();
|
||||
if (insn.getAttributes().contains(AttributeFlag.DECLARE_VAR)) {
|
||||
if (insn.contains(AFlag.DECLARE_VAR)) {
|
||||
declareVar(code, arg);
|
||||
} else {
|
||||
addArg(code, arg, false);
|
||||
@@ -121,9 +118,9 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
public void declareVar(CodeWriter code, RegisterArg arg) {
|
||||
code.add(useType(arg.getType()));
|
||||
useType(code, arg.getType());
|
||||
code.add(' ');
|
||||
code.add(mgen.assignArg(arg));
|
||||
code.add(mgen.getNameGen().assignArg(arg));
|
||||
}
|
||||
|
||||
private static String lit(LiteralArg arg) {
|
||||
@@ -133,42 +130,56 @@ public class InsnGen {
|
||||
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
|
||||
FieldNode fieldNode = mth.getParentClass().searchField(field);
|
||||
if (fieldNode != null) {
|
||||
FieldReplaceAttr replace = (FieldReplaceAttr) fieldNode.getAttributes().get(AttributeType.FIELD_REPLACE);
|
||||
FieldReplaceAttr replace = fieldNode.get(AType.FIELD_REPLACE);
|
||||
if (replace != null) {
|
||||
FieldInfo info = replace.getFieldInfo();
|
||||
if (replace.isOuterClass()) {
|
||||
code.add(useClass(info.getDeclClass())).add(".this");
|
||||
useClass(code, info.getDeclClass());
|
||||
code.add(".this");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
addArgDot(code, arg);
|
||||
fieldNode = mth.dex().resolveField(field);
|
||||
if (fieldNode != null) {
|
||||
code.attachAnnotation(fieldNode);
|
||||
}
|
||||
code.add(field.getName());
|
||||
}
|
||||
|
||||
public static String makeStaticFieldAccess(FieldInfo field, ClassGen clsGen) {
|
||||
public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) {
|
||||
ClassInfo declClass = field.getDeclClass();
|
||||
if (clsGen.getClassNode().getFullName().startsWith(declClass.getFullName())) {
|
||||
return field.getName();
|
||||
boolean fieldFromThisClass = clsGen.getClassNode().getFullName().startsWith(declClass.getFullName());
|
||||
if (!fieldFromThisClass) {
|
||||
// Android specific resources class handler
|
||||
ClassInfo parentClass = declClass.getParentClass();
|
||||
if (parentClass != null && parentClass.getShortName().equals("R")) {
|
||||
clsGen.useClass(code, parentClass);
|
||||
code.add('.');
|
||||
code.add(declClass.getShortName());
|
||||
} else {
|
||||
clsGen.useClass(code, declClass);
|
||||
}
|
||||
code.add('.');
|
||||
}
|
||||
// Android specific resources class handler
|
||||
ClassInfo parentClass = declClass.getParentClass();
|
||||
if (parentClass != null && parentClass.getShortName().equals("R")) {
|
||||
return clsGen.useClass(parentClass) + "." + declClass.getShortName() + "." + field.getName();
|
||||
FieldNode fieldNode = clsGen.getClassNode().dex().resolveField(field);
|
||||
if (fieldNode != null) {
|
||||
code.attachAnnotation(fieldNode);
|
||||
}
|
||||
return clsGen.useClass(declClass) + '.' + field.getName();
|
||||
code.add(field.getName());
|
||||
}
|
||||
|
||||
protected String staticField(FieldInfo field) {
|
||||
return makeStaticFieldAccess(field, mgen.getClassGen());
|
||||
protected void staticField(CodeWriter code, FieldInfo field) {
|
||||
makeStaticFieldAccess(code, field, mgen.getClassGen());
|
||||
}
|
||||
|
||||
public String useClass(ClassInfo cls) {
|
||||
return mgen.getClassGen().useClass(cls);
|
||||
public void useClass(CodeWriter code, ClassInfo cls) {
|
||||
mgen.getClassGen().useClass(code, cls);
|
||||
}
|
||||
|
||||
private String useType(ArgType type) {
|
||||
return TypeGen.translate(mgen.getClassGen(), type);
|
||||
private void useType(CodeWriter code, ArgType type) {
|
||||
mgen.getClassGen().useType(code, type);
|
||||
}
|
||||
|
||||
public boolean makeInsn(InsnNode insn, CodeWriter code) throws CodegenException {
|
||||
@@ -187,7 +198,7 @@ public class InsnGen {
|
||||
} else {
|
||||
code.startLine();
|
||||
if (insn.getSourceLine() != 0) {
|
||||
code.attachAnnotation(insn.getSourceLine());
|
||||
code.attachSourceLine(insn.getSourceLine());
|
||||
}
|
||||
if (insn.getResult() != null && insn.getType() != InsnType.ARITH_ONEARG) {
|
||||
assignVar(code, insn);
|
||||
@@ -211,7 +222,8 @@ public class InsnGen {
|
||||
|
||||
case CONST_CLASS:
|
||||
ArgType clsType = ((ConstClassNode) insn).getClsType();
|
||||
code.add(useType(clsType)).add(".class");
|
||||
useType(code, clsType);
|
||||
code.add(".class");
|
||||
break;
|
||||
|
||||
case CONST:
|
||||
@@ -230,7 +242,7 @@ public class InsnGen {
|
||||
code.add('(');
|
||||
}
|
||||
code.add('(');
|
||||
code.add(useType((ArgType) ((IndexInsnNode) insn).getIndex()));
|
||||
useType(code, (ArgType) ((IndexInsnNode) insn).getIndex());
|
||||
code.add(") ");
|
||||
addArg(code, insn.getArg(0), true);
|
||||
if (wrap) {
|
||||
@@ -244,7 +256,7 @@ public class InsnGen {
|
||||
break;
|
||||
|
||||
case ARITH_ONEARG:
|
||||
makeArithOneArg((ArithNode) insn, code, state);
|
||||
makeArithOneArg((ArithNode) insn, code);
|
||||
break;
|
||||
|
||||
case NEG: {
|
||||
@@ -302,14 +314,14 @@ public class InsnGen {
|
||||
}
|
||||
addArg(code, insn.getArg(0));
|
||||
code.add(" instanceof ");
|
||||
code.add(useType((ArgType) ((IndexInsnNode) insn).getIndex()));
|
||||
useType(code, (ArgType) ((IndexInsnNode) insn).getIndex());
|
||||
if (wrap) {
|
||||
code.add(')');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONSTRUCTOR:
|
||||
makeConstructor((ConstructorInsn) insn, code, state);
|
||||
makeConstructor((ConstructorInsn) insn, code);
|
||||
break;
|
||||
|
||||
case INVOKE:
|
||||
@@ -318,7 +330,8 @@ public class InsnGen {
|
||||
|
||||
case NEW_ARRAY: {
|
||||
ArgType arrayType = insn.getResult().getType();
|
||||
code.add("new ").add(useType(arrayType.getArrayRootElement()));
|
||||
code.add("new ");
|
||||
useType(code, arrayType.getArrayRootElement());
|
||||
code.add('[');
|
||||
addArg(code, insn.getArg(0));
|
||||
code.add(']');
|
||||
@@ -371,11 +384,12 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
case SGET:
|
||||
code.add(staticField((FieldInfo) ((IndexInsnNode) insn).getIndex()));
|
||||
staticField(code, (FieldInfo) ((IndexInsnNode) insn).getIndex());
|
||||
break;
|
||||
case SPUT:
|
||||
FieldInfo field = (FieldInfo) ((IndexInsnNode) insn).getIndex();
|
||||
code.add(staticField(field)).add(" = ");
|
||||
staticField(code, field);
|
||||
code.add(" = ");
|
||||
addArg(code, insn.getArg(0), false);
|
||||
break;
|
||||
|
||||
@@ -427,6 +441,9 @@ public class InsnGen {
|
||||
addArg(code, insn.getArg(0));
|
||||
break;
|
||||
|
||||
case PHI:
|
||||
break;
|
||||
|
||||
/* fallback mode instructions */
|
||||
case IF:
|
||||
assert isFallback() : "if insn in not fallback mode";
|
||||
@@ -452,7 +469,8 @@ public class InsnGen {
|
||||
code.add(") {");
|
||||
code.incIndent();
|
||||
for (int i = 0; i < sw.getCasesCount(); i++) {
|
||||
code.startLine("case ").add(sw.getKeys()[i]).add(": goto ");
|
||||
String key = sw.getKeys()[i].toString();
|
||||
code.startLine("case ").add(key).add(": goto ");
|
||||
code.add(MethodGen.getLabelName(sw.getTargets()[i])).add(';');
|
||||
}
|
||||
code.startLine("default: goto ");
|
||||
@@ -474,10 +492,11 @@ public class InsnGen {
|
||||
|
||||
private void filledNewArray(InsnNode insn, CodeWriter code) throws CodegenException {
|
||||
int c = insn.getArgsCount();
|
||||
code.add("new ").add(useType(insn.getResult().getType()));
|
||||
code.add("new ");
|
||||
useType(code, insn.getResult().getType());
|
||||
code.add('{');
|
||||
for (int i = 0; i < c; i++) {
|
||||
addArg(code, insn.getArg(i));
|
||||
addArg(code, insn.getArg(i), false);
|
||||
if (i + 1 < c) {
|
||||
code.add(", ");
|
||||
}
|
||||
@@ -539,13 +558,15 @@ public class InsnGen {
|
||||
}
|
||||
int len = str.length();
|
||||
str.delete(len - 2, len);
|
||||
code.add("new ").add(useType(elType)).add("[]{").add(str.toString()).add('}');
|
||||
code.add("new ");
|
||||
useType(code, elType);
|
||||
code.add("[]{").add(str.toString()).add('}');
|
||||
}
|
||||
|
||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code, EnumSet<Flags> state)
|
||||
private void makeConstructor(ConstructorInsn insn, CodeWriter code)
|
||||
throws CodegenException {
|
||||
ClassNode cls = mth.dex().resolveClass(insn.getClassType());
|
||||
if (cls != null && cls.isAnonymous()) {
|
||||
if (cls != null && cls.isAnonymous() && !fallback) {
|
||||
// anonymous class construction
|
||||
ClassInfo parent;
|
||||
if (cls.getInterfaces().size() == 1) {
|
||||
@@ -553,14 +574,22 @@ public class InsnGen {
|
||||
} else {
|
||||
parent = cls.getSuperClass();
|
||||
}
|
||||
cls.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
MethodNode defCtr = cls.getDefaultConstructor();
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.getAttributes().add(AttributeFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
if (defCtr != null) {
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.add(AFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.add(AFlag.DONT_GENERATE);
|
||||
}
|
||||
}
|
||||
code.add("new ").add(parent == null ? "Object" : useClass(parent)).add("() ");
|
||||
code.add("new ");
|
||||
if (parent == null) {
|
||||
code.add("Object");
|
||||
} else {
|
||||
useClass(code, parent);
|
||||
}
|
||||
code.add("() ");
|
||||
new ClassGen(cls, mgen.getClassGen().getParentGen(), fallback).addClassBody(code);
|
||||
return;
|
||||
}
|
||||
@@ -572,7 +601,8 @@ public class InsnGen {
|
||||
} else if (insn.isThis()) {
|
||||
code.add("this");
|
||||
} else {
|
||||
code.add("new ").add(useClass(insn.getClassType()));
|
||||
code.add("new ");
|
||||
useClass(code, insn.getClassType());
|
||||
}
|
||||
generateArguments(code, insn, 0, mth.dex().resolveMethod(insn.getCallMth()));
|
||||
}
|
||||
@@ -582,9 +612,7 @@ public class InsnGen {
|
||||
|
||||
// inline method
|
||||
MethodNode callMthNode = mth.dex().resolveMethod(callMth);
|
||||
if (callMthNode != null
|
||||
&& callMthNode.getAttributes().contains(AttributeType.METHOD_INLINE)) {
|
||||
inlineMethod(callMthNode, insn, code);
|
||||
if (callMthNode != null && inlineMethod(callMthNode, insn, code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -612,7 +640,8 @@ public class InsnGen {
|
||||
ClassInfo insnCls = mth.getParentClass().getClassInfo();
|
||||
ClassInfo declClass = callMth.getDeclClass();
|
||||
if (!insnCls.equals(declClass)) {
|
||||
code.add(useClass(declClass)).add('.');
|
||||
useClass(code, declClass);
|
||||
code.add('.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -624,7 +653,7 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
private void generateArguments(CodeWriter code, InsnNode insn, int k, MethodNode callMth) throws CodegenException {
|
||||
if (callMth != null && callMth.getAttributes().contains(AttributeFlag.SKIP_FIRST_ARG)) {
|
||||
if (callMth != null && callMth.contains(AFlag.SKIP_FIRST_ARG)) {
|
||||
k++;
|
||||
}
|
||||
int argsCount = insn.getArgsCount();
|
||||
@@ -637,7 +666,9 @@ public class InsnGen {
|
||||
InsnArg arg = insn.getArg(i);
|
||||
ArgType origType = originalType.get(origPos);
|
||||
if (!arg.getType().equals(origType)) {
|
||||
code.add('(').add(useType(origType)).add(')');
|
||||
code.add('(');
|
||||
useType(code, origType);
|
||||
code.add(')');
|
||||
addArg(code, arg, true);
|
||||
} else {
|
||||
addArg(code, arg, false);
|
||||
@@ -661,9 +692,12 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
IAttribute mia = callMthNode.getAttributes().get(AttributeType.METHOD_INLINE);
|
||||
InsnNode inl = ((MethodInlineAttr) mia).getInsn();
|
||||
private boolean inlineMethod(MethodNode callMthNode, InvokeNode insn, CodeWriter code) throws CodegenException {
|
||||
MethodInlineAttr mia = callMthNode.get(AType.METHOD_INLINE);
|
||||
if (mia == null) {
|
||||
return false;
|
||||
}
|
||||
InsnNode inl = mia.getInsn();
|
||||
if (callMthNode.getMethodInfo().getArgumentsTypes().isEmpty()) {
|
||||
makeInsn(inl, code, Flags.BODY_ONLY);
|
||||
} else {
|
||||
@@ -680,12 +714,13 @@ public class InsnGen {
|
||||
inl.getRegisterArgs(inlArgs);
|
||||
Map<RegisterArg, InsnArg> toRevert = new HashMap<RegisterArg, InsnArg>();
|
||||
for (RegisterArg r : inlArgs) {
|
||||
if (r.getRegNum() >= regs.length) {
|
||||
LOG.warn("Unknown register number {} in method call: {}, {}", r, callMthNode, mth);
|
||||
int regNum = r.getRegNum();
|
||||
if (regNum >= regs.length) {
|
||||
LOG.warn("Unknown register number {} in method call: {} from {}", r, callMthNode, mth);
|
||||
} else {
|
||||
InsnArg repl = regs[r.getRegNum()];
|
||||
InsnArg repl = regs[regNum];
|
||||
if (repl == null) {
|
||||
LOG.warn("Not passed register {} in method call: {}, {}", r, callMthNode, mth);
|
||||
LOG.warn("Not passed register {} in method call: {} from {}", r, callMthNode, mth);
|
||||
} else {
|
||||
inl.replaceArg(r, repl);
|
||||
toRevert.put(r, repl);
|
||||
@@ -693,11 +728,12 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
makeInsn(inl, code, Flags.BODY_ONLY);
|
||||
// revert changes
|
||||
// revert changes in 'MethodInlineAttr'
|
||||
for (Map.Entry<RegisterArg, InsnArg> e : toRevert.entrySet()) {
|
||||
inl.replaceArg(e.getValue(), e.getKey());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void makeTernary(TernaryInsn insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
|
||||
@@ -724,8 +760,7 @@ public class InsnGen {
|
||||
|
||||
private void makeArith(ArithNode insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
|
||||
// wrap insn in brackets for save correct operation order
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY)
|
||||
&& !insn.getAttributes().contains(AttributeFlag.DONT_WRAP);
|
||||
boolean wrap = state.contains(Flags.BODY_ONLY) && !insn.contains(AFlag.DONT_WRAP);
|
||||
if (wrap) {
|
||||
code.add('(');
|
||||
}
|
||||
@@ -739,7 +774,7 @@ public class InsnGen {
|
||||
}
|
||||
}
|
||||
|
||||
private void makeArithOneArg(ArithNode insn, CodeWriter code, EnumSet<Flags> state) throws CodegenException {
|
||||
private void makeArithOneArg(ArithNode insn, CodeWriter code) throws CodegenException {
|
||||
ArithOp op = insn.getOp();
|
||||
InsnArg arg = insn.getArg(0);
|
||||
// "++" or "--"
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.AttributesList;
|
||||
import jadx.core.dex.attributes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.regions.Region;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.FallbackModeVisitor;
|
||||
@@ -22,10 +18,8 @@ import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -37,27 +31,24 @@ public class MethodGen {
|
||||
|
||||
private final MethodNode mth;
|
||||
private final ClassGen classGen;
|
||||
private final boolean fallback;
|
||||
private final AnnotationGen annotationGen;
|
||||
|
||||
private final Set<String> varNames = new HashSet<String>();
|
||||
private final NameGen nameGen;
|
||||
|
||||
public MethodGen(ClassGen classGen, MethodNode mth) {
|
||||
this.mth = mth;
|
||||
this.classGen = classGen;
|
||||
this.fallback = classGen.isFallbackMode();
|
||||
this.annotationGen = classGen.getAnnotationGen();
|
||||
|
||||
List<RegisterArg> args = mth.getArguments(true);
|
||||
for (RegisterArg arg : args) {
|
||||
varNames.add(makeArgName(arg));
|
||||
}
|
||||
this.nameGen = new NameGen(classGen.isFallbackMode());
|
||||
}
|
||||
|
||||
public ClassGen getClassGen() {
|
||||
return classGen;
|
||||
}
|
||||
|
||||
public NameGen getNameGen() {
|
||||
return nameGen;
|
||||
}
|
||||
|
||||
public MethodNode getMethodNode() {
|
||||
return mth;
|
||||
}
|
||||
@@ -68,7 +59,7 @@ public class MethodGen {
|
||||
code.attachDefinition(mth);
|
||||
return true;
|
||||
}
|
||||
if (mth.getAttributes().contains(AttributeFlag.ANONYMOUS_CONSTRUCTOR)) {
|
||||
if (mth.contains(AFlag.ANONYMOUS_CONSTRUCTOR)) {
|
||||
// don't add method name and arguments
|
||||
code.startLine();
|
||||
code.attachDefinition(mth);
|
||||
@@ -78,15 +69,17 @@ public class MethodGen {
|
||||
|
||||
AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags();
|
||||
AccessInfo ai = mth.getAccessFlags();
|
||||
// don't add 'abstract' to methods in interface
|
||||
// don't add 'abstract' and 'public' to methods in interface
|
||||
if (clsAccFlags.isInterface()) {
|
||||
ai = ai.remove(AccessFlags.ACC_ABSTRACT);
|
||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||
}
|
||||
// don't add 'public' for annotations
|
||||
if (clsAccFlags.isAnnotation()) {
|
||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||
}
|
||||
code.startLine(ai.makeString());
|
||||
code.attachSourceLine(mth.getSourceLine());
|
||||
|
||||
if (classGen.addGenericMap(code, mth.getGenericMap())) {
|
||||
code.add(' ');
|
||||
@@ -94,7 +87,7 @@ public class MethodGen {
|
||||
if (mth.getAccessFlags().isConstructor()) {
|
||||
code.add(classGen.getClassNode().getShortName()); // constructor
|
||||
} else {
|
||||
code.add(TypeGen.translate(classGen, mth.getReturnType()));
|
||||
classGen.useType(code, mth.getReturnType());
|
||||
code.add(' ');
|
||||
code.add(mth.getName());
|
||||
}
|
||||
@@ -102,7 +95,7 @@ public class MethodGen {
|
||||
|
||||
List<RegisterArg> args = mth.getArguments(false);
|
||||
if (mth.getMethodInfo().isConstructor()
|
||||
&& mth.getParentClass().getAttributes().contains(AttributeType.ENUM_CLASS)) {
|
||||
&& mth.getParentClass().contains(AType.ENUM_CLASS)) {
|
||||
if (args.size() == 2) {
|
||||
args.clear();
|
||||
} else if (args.size() > 2) {
|
||||
@@ -123,9 +116,7 @@ public class MethodGen {
|
||||
}
|
||||
|
||||
private void addMethodArguments(CodeWriter argsCode, List<RegisterArg> args) {
|
||||
MethodParameters paramsAnnotation =
|
||||
(MethodParameters) mth.getAttributes().get(AttributeType.ANNOTATION_MTH_PARAMETERS);
|
||||
|
||||
MethodParameters paramsAnnotation = mth.get(AType.ANNOTATION_MTH_PARAMETERS);
|
||||
int i = 0;
|
||||
for (Iterator<RegisterArg> it = args.iterator(); it.hasNext(); ) {
|
||||
RegisterArg arg = it.next();
|
||||
@@ -139,17 +130,17 @@ public class MethodGen {
|
||||
ArgType type = arg.getType();
|
||||
if (type.isArray()) {
|
||||
ArgType elType = type.getArrayElement();
|
||||
argsCode.add(TypeGen.translate(classGen, elType));
|
||||
argsCode.add(" ...");
|
||||
classGen.useType(argsCode, elType);
|
||||
argsCode.add("...");
|
||||
} else {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth, "Last argument in varargs method not array"));
|
||||
argsCode.add(TypeGen.translate(classGen, arg.getType()));
|
||||
classGen.useType(argsCode, arg.getType());
|
||||
}
|
||||
} else {
|
||||
argsCode.add(TypeGen.translate(classGen, arg.getType()));
|
||||
classGen.useType(argsCode, arg.getType());
|
||||
}
|
||||
argsCode.add(' ');
|
||||
argsCode.add(makeArgName(arg));
|
||||
argsCode.add(nameGen.assignArg(arg));
|
||||
|
||||
i++;
|
||||
if (it.hasNext()) {
|
||||
@@ -158,117 +149,34 @@ public class MethodGen {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make variable name for register,
|
||||
* Name contains register number and
|
||||
* variable type or name (if debug info available)
|
||||
*/
|
||||
public String makeArgName(RegisterArg arg) {
|
||||
String name = arg.getTypedVar().getName();
|
||||
String base = "r" + arg.getRegNum();
|
||||
if (fallback) {
|
||||
if (name != null) {
|
||||
return base + "_" + name;
|
||||
}
|
||||
return base;
|
||||
} else {
|
||||
if (name != null) {
|
||||
if (Consts.DEBUG) {
|
||||
return base + "_" + name;
|
||||
}
|
||||
return name;
|
||||
} else {
|
||||
ArgType type = arg.getType();
|
||||
if (type.isPrimitive()) {
|
||||
return base + type.getPrimitiveType().getShortName().toLowerCase();
|
||||
} else {
|
||||
return base + "_" + Utils.escape(TypeGen.translate(classGen, arg.getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put variable declaration and return variable name (used for assignments)
|
||||
*
|
||||
* @param arg register variable
|
||||
* @return variable name
|
||||
*/
|
||||
public String assignArg(RegisterArg arg) {
|
||||
String name = makeArgName(arg);
|
||||
if (varNames.add(name) || fallback) {
|
||||
return name;
|
||||
}
|
||||
name = getUniqVarName(name);
|
||||
arg.getTypedVar().setName(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
public String assignNamedArg(NamedArg arg) {
|
||||
String name = arg.getName();
|
||||
if (varNames.add(name) || fallback) {
|
||||
return name;
|
||||
}
|
||||
name = getUniqVarName(name);
|
||||
arg.setName(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
private String getUniqVarName(String name) {
|
||||
String r;
|
||||
int i = 2;
|
||||
do {
|
||||
r = name + "_" + i;
|
||||
i++;
|
||||
} while (varNames.contains(r));
|
||||
varNames.add(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
public void addInstructions(CodeWriter code) throws CodegenException {
|
||||
if (mth.getAttributes().contains(AttributeType.JADX_ERROR)) {
|
||||
code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ");
|
||||
code.add(mth.toString());
|
||||
code.add("\");");
|
||||
if (mth.contains(AType.JADX_ERROR)
|
||||
|| mth.contains(AFlag.INCONSISTENT_CODE)
|
||||
|| mth.getRegion() == null) {
|
||||
code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ")
|
||||
.add(mth.toString())
|
||||
.add("\");");
|
||||
|
||||
JadxErrorAttr err = (JadxErrorAttr) mth.getAttributes().get(AttributeType.JADX_ERROR);
|
||||
code.startLine("/* JADX: method processing error */");
|
||||
Throwable cause = err.getCause();
|
||||
if (cause != null) {
|
||||
code.newLine();
|
||||
code.add("/*");
|
||||
code.startLine("Error: ").add(Utils.getStackTrace(cause));
|
||||
code.add("*/");
|
||||
if (mth.contains(AType.JADX_ERROR)) {
|
||||
JadxErrorAttr err = mth.get(AType.JADX_ERROR);
|
||||
code.startLine("/* JADX: method processing error */");
|
||||
Throwable cause = err.getCause();
|
||||
if (cause != null) {
|
||||
code.newLine();
|
||||
code.add("/*");
|
||||
code.startLine("Error: ").add(Utils.getStackTrace(cause));
|
||||
code.add("*/");
|
||||
}
|
||||
}
|
||||
makeMethodDump(code);
|
||||
} else if (mth.getAttributes().contains(AttributeFlag.INCONSISTENT_CODE)) {
|
||||
code.startLine("/*");
|
||||
addFallbackMethodCode(code);
|
||||
code.startLine("*/");
|
||||
code.newLine();
|
||||
} else {
|
||||
Region startRegion = mth.getRegion();
|
||||
if (startRegion != null) {
|
||||
(new RegionGen(this)).makeRegion(code, startRegion);
|
||||
} else {
|
||||
addFallbackMethodCode(code);
|
||||
}
|
||||
RegionGen regionGen = new RegionGen(this);
|
||||
regionGen.makeRegion(code, mth.getRegion());
|
||||
}
|
||||
}
|
||||
|
||||
private void makeMethodDump(CodeWriter code) {
|
||||
code.startLine("/*");
|
||||
getFallbackMethodGen(mth).addDefinition(code);
|
||||
code.add(" {");
|
||||
code.incIndent();
|
||||
|
||||
addFallbackMethodCode(code);
|
||||
|
||||
code.decIndent();
|
||||
code.startLine('}');
|
||||
code.startLine("*/");
|
||||
}
|
||||
|
||||
public void addFallbackMethodCode(CodeWriter code) {
|
||||
if (mth.getInstructions() == null) {
|
||||
// load original instructions
|
||||
@@ -281,24 +189,26 @@ public class MethodGen {
|
||||
return;
|
||||
}
|
||||
}
|
||||
List<InsnNode> insns = mth.getInstructions();
|
||||
if (insns == null) {
|
||||
InsnNode[] insnArr = mth.getInstructions();
|
||||
if (insnArr == null) {
|
||||
code.startLine("// Can't load method instructions.");
|
||||
return;
|
||||
}
|
||||
if (mth.getThisArg() != null) {
|
||||
code.startLine(getFallbackMethodGen(mth).makeArgName(mth.getThisArg())).add(" = this;");
|
||||
code.startLine(nameGen.useArg(mth.getThisArg())).add(" = this;");
|
||||
}
|
||||
addFallbackInsns(code, mth, insns, true);
|
||||
addFallbackInsns(code, mth, insnArr, true);
|
||||
}
|
||||
|
||||
public static void addFallbackInsns(CodeWriter code, MethodNode mth, List<InsnNode> insns, boolean addLabels) {
|
||||
public static void addFallbackInsns(CodeWriter code, MethodNode mth, InsnNode[] insnArr, boolean addLabels) {
|
||||
InsnGen insnGen = new InsnGen(getFallbackMethodGen(mth), true);
|
||||
for (InsnNode insn : insns) {
|
||||
AttributesList attrs = insn.getAttributes();
|
||||
for (InsnNode insn : insnArr) {
|
||||
if (insn == null) {
|
||||
continue;
|
||||
}
|
||||
if (addLabels) {
|
||||
if (attrs.contains(AttributeType.JUMP)
|
||||
|| attrs.contains(AttributeType.EXC_HANDLER)) {
|
||||
if (insn.contains(AType.JUMP)
|
||||
|| insn.contains(AType.EXC_HANDLER)) {
|
||||
code.decIndent();
|
||||
code.startLine(getLabelName(insn.getOffset()) + ":");
|
||||
code.incIndent();
|
||||
@@ -306,12 +216,13 @@ public class MethodGen {
|
||||
}
|
||||
try {
|
||||
if (insnGen.makeInsn(insn, code)) {
|
||||
CatchAttr catchAttr = (CatchAttr) attrs.get(AttributeType.CATCH_BLOCK);
|
||||
CatchAttr catchAttr = insn.get(AType.CATCH_BLOCK);
|
||||
if (catchAttr != null) {
|
||||
code.add("\t //" + catchAttr);
|
||||
code.add("\t " + catchAttr);
|
||||
}
|
||||
}
|
||||
} catch (CodegenException e) {
|
||||
LOG.debug("Error generate fallback instruction: ", e.getCause());
|
||||
code.startLine("// error: " + insn);
|
||||
}
|
||||
}
|
||||
@@ -320,7 +231,7 @@ public class MethodGen {
|
||||
/**
|
||||
* Return fallback variant of method codegen
|
||||
*/
|
||||
private static MethodGen getFallbackMethodGen(MethodNode mth) {
|
||||
static MethodGen getFallbackMethodGen(MethodNode mth) {
|
||||
ClassGen clsGen = new ClassGen(mth.getParentClass(), null, true);
|
||||
return new MethodGen(clsGen, mth);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.deobf.NameMapper;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||
import jadx.core.dex.instructions.args.NamedArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class NameGen {
|
||||
|
||||
private static final Map<String, String> OBJ_ALIAS;
|
||||
|
||||
private final Set<String> varNames = new HashSet<String>();
|
||||
private final boolean fallback;
|
||||
|
||||
static {
|
||||
OBJ_ALIAS = new HashMap<String, String>();
|
||||
OBJ_ALIAS.put(Consts.CLASS_STRING, "str");
|
||||
OBJ_ALIAS.put(Consts.CLASS_CLASS, "cls");
|
||||
OBJ_ALIAS.put(Consts.CLASS_THROWABLE, "th");
|
||||
OBJ_ALIAS.put(Consts.CLASS_OBJECT, "obj");
|
||||
OBJ_ALIAS.put("java.util.Iterator", "it");
|
||||
OBJ_ALIAS.put("java.lang.Boolean", "bool");
|
||||
OBJ_ALIAS.put("java.lang.Short", "sh");
|
||||
OBJ_ALIAS.put("java.lang.Integer", "num");
|
||||
OBJ_ALIAS.put("java.lang.Character", "ch");
|
||||
OBJ_ALIAS.put("java.lang.Byte", "b");
|
||||
OBJ_ALIAS.put("java.lang.Float", "f");
|
||||
OBJ_ALIAS.put("java.lang.Long", "l");
|
||||
OBJ_ALIAS.put("java.lang.Double", "d");
|
||||
}
|
||||
|
||||
public NameGen(boolean fallback) {
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
public String assignArg(RegisterArg arg) {
|
||||
String name = makeArgName(arg);
|
||||
if (fallback) {
|
||||
return name;
|
||||
}
|
||||
name = getUniqueVarName(name);
|
||||
SSAVar sVar = arg.getSVar();
|
||||
if (sVar != null) {
|
||||
sVar.setName(name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public String assignNamedArg(NamedArg arg) {
|
||||
String name = arg.getName();
|
||||
if (fallback) {
|
||||
return name;
|
||||
}
|
||||
name = getUniqueVarName(name);
|
||||
arg.setName(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
public String useArg(RegisterArg arg) {
|
||||
String name = makeArgName(arg);
|
||||
varNames.add(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
private String getUniqueVarName(String name) {
|
||||
String r = name;
|
||||
int i = 2;
|
||||
while (varNames.contains(r)) {
|
||||
r = name + i;
|
||||
i++;
|
||||
}
|
||||
varNames.add(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
private String makeArgName(RegisterArg arg) {
|
||||
String name = arg.getName();
|
||||
if (fallback) {
|
||||
String base = "r" + arg.getRegNum();
|
||||
if (name != null && !name.equals("this")) {
|
||||
return base + "_" + name;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
String varName;
|
||||
if (name != null) {
|
||||
if ("this".equals(name)) {
|
||||
return name;
|
||||
}
|
||||
varName = name;
|
||||
} else {
|
||||
varName = makeNameForType(arg.getType());
|
||||
}
|
||||
if (NameMapper.isReserved(varName)) {
|
||||
return varName + "R";
|
||||
}
|
||||
return varName;
|
||||
}
|
||||
|
||||
private static String makeNameForType(ArgType type) {
|
||||
if (type.isPrimitive()) {
|
||||
return makeNameForPrimitive(type);
|
||||
} else if (type.isArray()) {
|
||||
return makeNameForType(type.getArrayRootElement()) + "Arr";
|
||||
} else {
|
||||
return makeNameForObject(type);
|
||||
}
|
||||
}
|
||||
|
||||
private static String makeNameForPrimitive(ArgType type) {
|
||||
return type.getPrimitiveType().getShortName().toLowerCase();
|
||||
}
|
||||
|
||||
private static String makeNameForObject(ArgType type) {
|
||||
if (type.isObject()) {
|
||||
String alias = getAliasForObject(type.getObject());
|
||||
if (alias != null) {
|
||||
return alias;
|
||||
}
|
||||
ClassInfo clsInfo = ClassInfo.fromType(type);
|
||||
String shortName = clsInfo.getShortName();
|
||||
String vName = fromName(shortName);
|
||||
if (vName != null) {
|
||||
return vName;
|
||||
}
|
||||
}
|
||||
return Utils.escape(type.toString());
|
||||
}
|
||||
|
||||
private static String fromName(String name) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (name.toUpperCase().equals(name)) {
|
||||
// all characters are upper case
|
||||
return name.toLowerCase();
|
||||
}
|
||||
String v1 = Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
if (!v1.equals(name)) {
|
||||
return v1;
|
||||
}
|
||||
if (name.length() < 3) {
|
||||
return name + "Var";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void guessName(RegisterArg arg) {
|
||||
SSAVar sVar = arg.getSVar();
|
||||
if (sVar == null || sVar.getName() != null) {
|
||||
return;
|
||||
}
|
||||
RegisterArg assignArg = sVar.getAssign();
|
||||
InsnNode assignInsn = assignArg.getParentInsn();
|
||||
String name = makeNameFromInsn(assignInsn);
|
||||
if (name != null && !NameMapper.isReserved(name)) {
|
||||
assignArg.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getAliasForObject(String name) {
|
||||
return OBJ_ALIAS.get(name);
|
||||
}
|
||||
|
||||
private static String makeNameFromInsn(InsnNode insn) {
|
||||
switch (insn.getType()) {
|
||||
case INVOKE:
|
||||
InvokeNode inv = (InvokeNode) insn;
|
||||
String name = inv.getCallMth().getName();
|
||||
if (name.startsWith("get") || name.startsWith("set")) {
|
||||
return fromName(name.substring(3));
|
||||
}
|
||||
if ("iterator".equals(name)) {
|
||||
return "it";
|
||||
}
|
||||
return name;
|
||||
|
||||
case CONSTRUCTOR:
|
||||
ConstructorInsn co = (ConstructorInsn) insn;
|
||||
return makeNameForObject(co.getClassType().getType());
|
||||
|
||||
case ARRAY_LENGTH:
|
||||
return "length";
|
||||
|
||||
case ARITH:
|
||||
case TERNARY:
|
||||
case CAST:
|
||||
for (InsnArg arg : insn.getArguments()) {
|
||||
if (arg.isInsnWrap()) {
|
||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||
String wName = makeNameFromInsn(wrapInsn);
|
||||
if (wName != null) {
|
||||
return wName;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.attributes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.SwitchNode;
|
||||
@@ -43,17 +42,19 @@ public class RegionGen extends InsnGen {
|
||||
if (cont instanceof IBlock) {
|
||||
makeSimpleBlock((IBlock) cont, code);
|
||||
} else if (cont instanceof IRegion) {
|
||||
declareVars(code, cont);
|
||||
if (cont instanceof Region) {
|
||||
makeSimpleRegion(code, (Region) cont);
|
||||
} else if (cont instanceof IfRegion) {
|
||||
makeIf((IfRegion) cont, code, true);
|
||||
} else if (cont instanceof SwitchRegion) {
|
||||
makeSwitch((SwitchRegion) cont, code);
|
||||
} else if (cont instanceof LoopRegion) {
|
||||
makeLoop((LoopRegion) cont, code);
|
||||
} else if (cont instanceof SynchronizedRegion) {
|
||||
makeSynchronizedRegion((SynchronizedRegion) cont, code);
|
||||
} else {
|
||||
declareVars(code, cont);
|
||||
if (cont instanceof IfRegion) {
|
||||
makeIf((IfRegion) cont, code, true);
|
||||
} else if (cont instanceof SwitchRegion) {
|
||||
makeSwitch((SwitchRegion) cont, code);
|
||||
} else if (cont instanceof LoopRegion) {
|
||||
makeLoop((LoopRegion) cont, code);
|
||||
} else if (cont instanceof SynchronizedRegion) {
|
||||
makeSynchronizedRegion((SynchronizedRegion) cont, code);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new CodegenException("Not processed container: " + cont);
|
||||
@@ -61,8 +62,7 @@ public class RegionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private void declareVars(CodeWriter code, IContainer cont) {
|
||||
DeclareVariablesAttr declVars =
|
||||
(DeclareVariablesAttr) cont.getAttributes().get(AttributeType.DECLARE_VARIABLES);
|
||||
DeclareVariablesAttr declVars = cont.get(AType.DECLARE_VARIABLES);
|
||||
if (declVars != null) {
|
||||
for (RegisterArg v : declVars.getVars()) {
|
||||
code.startLine();
|
||||
@@ -73,10 +73,11 @@ public class RegionGen extends InsnGen {
|
||||
}
|
||||
|
||||
private void makeSimpleRegion(CodeWriter code, Region region) throws CodegenException {
|
||||
CatchAttr tc = (CatchAttr) region.getAttributes().get(AttributeType.CATCH_BLOCK);
|
||||
CatchAttr tc = region.get(AType.CATCH_BLOCK);
|
||||
if (tc != null) {
|
||||
makeTryCatch(region, tc.getTryBlock(), code);
|
||||
} else {
|
||||
declareVars(code, region);
|
||||
for (IContainer c : region.getSubBlocks()) {
|
||||
makeRegion(code, c);
|
||||
}
|
||||
@@ -93,9 +94,8 @@ public class RegionGen extends InsnGen {
|
||||
for (InsnNode insn : block.getInstructions()) {
|
||||
makeInsn(insn, code);
|
||||
}
|
||||
IAttribute attr;
|
||||
if ((attr = block.getAttributes().get(AttributeType.FORCE_RETURN)) != null) {
|
||||
ForceReturnAttr retAttr = (ForceReturnAttr) attr;
|
||||
ForceReturnAttr retAttr = block.get(AType.FORCE_RETURN);
|
||||
if (retAttr != null) {
|
||||
makeInsn(retAttr.getReturnInsn(), code);
|
||||
}
|
||||
}
|
||||
@@ -108,6 +108,7 @@ public class RegionGen extends InsnGen {
|
||||
if (newLine) {
|
||||
code.startLine();
|
||||
}
|
||||
code.attachSourceLine(region.getSourceLine());
|
||||
code.add("if (");
|
||||
new ConditionGen(this).add(code, region.getCondition());
|
||||
code.add(") {");
|
||||
@@ -130,16 +131,17 @@ public class RegionGen extends InsnGen {
|
||||
* Connect if-else-if block
|
||||
*/
|
||||
private boolean connectElseIf(CodeWriter code, IContainer els) throws CodegenException {
|
||||
if (els instanceof Region) {
|
||||
Region re = (Region) els;
|
||||
List<IContainer> subBlocks = re.getSubBlocks();
|
||||
if (subBlocks.size() == 1 && subBlocks.get(0) instanceof IfRegion) {
|
||||
IfRegion ifRegion = (IfRegion) subBlocks.get(0);
|
||||
if (ifRegion.getAttributes().contains(AttributeFlag.ELSE_IF_CHAIN)) {
|
||||
makeIf(ifRegion, code, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!els.contains(AFlag.ELSE_IF_CHAIN)) {
|
||||
return false;
|
||||
}
|
||||
if (!(els instanceof Region)) {
|
||||
return false;
|
||||
}
|
||||
List<IContainer> subBlocks = ((Region) els).getSubBlocks();
|
||||
if (subBlocks.size() == 1
|
||||
&& subBlocks.get(0) instanceof IfRegion) {
|
||||
makeIf((IfRegion) subBlocks.get(0), code, false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -150,7 +152,7 @@ public class RegionGen extends InsnGen {
|
||||
List<InsnNode> headerInsns = header.getInstructions();
|
||||
if (headerInsns.size() > 1) {
|
||||
// write not inlined instructions from header
|
||||
mth.getAttributes().add(AttributeFlag.INCONSISTENT_CODE);
|
||||
mth.add(AFlag.INCONSISTENT_CODE);
|
||||
int last = headerInsns.size() - 1;
|
||||
for (int i = 0; i < last; i++) {
|
||||
InsnNode insn = headerInsns.get(i);
|
||||
@@ -197,7 +199,7 @@ public class RegionGen extends InsnGen {
|
||||
SwitchNode insn = (SwitchNode) sw.getHeader().getInstructions().get(0);
|
||||
InsnArg arg = insn.getArg(0);
|
||||
code.startLine("switch (");
|
||||
addArg(code, arg);
|
||||
addArg(code, arg, false);
|
||||
code.add(") {");
|
||||
code.incIndent();
|
||||
|
||||
@@ -208,7 +210,7 @@ public class RegionGen extends InsnGen {
|
||||
for (Object k : keys) {
|
||||
code.startLine("case ");
|
||||
if (k instanceof IndexInsnNode) {
|
||||
code.add(staticField((FieldInfo) ((IndexInsnNode) k).getIndex()));
|
||||
staticField(code, (FieldInfo) ((IndexInsnNode) k).getIndex());
|
||||
} else {
|
||||
code.add(TypeGen.literalToString((Integer) k, arg.getType()));
|
||||
}
|
||||
@@ -241,7 +243,7 @@ public class RegionGen extends InsnGen {
|
||||
private void makeTryCatch(IContainer region, TryCatchBlock tryCatchBlock, CodeWriter code)
|
||||
throws CodegenException {
|
||||
code.startLine("try {");
|
||||
region.getAttributes().remove(AttributeType.CATCH_BLOCK);
|
||||
region.remove(AType.CATCH_BLOCK);
|
||||
makeRegionIndent(code, region);
|
||||
ExceptionHandler allHandler = null;
|
||||
for (ExceptionHandler handler : tryCatchBlock.getHandlers()) {
|
||||
@@ -257,9 +259,9 @@ public class RegionGen extends InsnGen {
|
||||
if (allHandler != null) {
|
||||
makeCatchBlock(code, allHandler);
|
||||
}
|
||||
if (tryCatchBlock.getFinalBlock() != null) {
|
||||
if (tryCatchBlock.getFinalRegion() != null) {
|
||||
code.startLine("} finally {");
|
||||
makeRegionIndent(code, tryCatchBlock.getFinalBlock());
|
||||
makeRegionIndent(code, tryCatchBlock.getFinalRegion());
|
||||
}
|
||||
code.startLine('}');
|
||||
}
|
||||
@@ -269,9 +271,13 @@ public class RegionGen extends InsnGen {
|
||||
IContainer region = handler.getHandlerRegion();
|
||||
if (region != null) {
|
||||
code.startLine("} catch (");
|
||||
code.add(handler.isCatchAll() ? "Throwable" : useClass(handler.getCatchType()));
|
||||
if (handler.isCatchAll()) {
|
||||
code.add("Throwable");
|
||||
} else {
|
||||
useClass(code, handler.getCatchType());
|
||||
}
|
||||
code.add(' ');
|
||||
code.add(mgen.assignNamedArg(handler.getArg()));
|
||||
code.add(mgen.getNameGen().assignNamedArg(handler.getArg()));
|
||||
code.add(") {");
|
||||
makeRegionIndent(code, region);
|
||||
}
|
||||
|
||||
@@ -8,22 +8,8 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class TypeGen {
|
||||
|
||||
public static String translate(ClassGen clsGen, ArgType type) {
|
||||
final PrimitiveType stype = type.getPrimitiveType();
|
||||
if (stype == null) {
|
||||
return type.toString();
|
||||
}
|
||||
if (stype == PrimitiveType.OBJECT) {
|
||||
return clsGen.useClass(type);
|
||||
}
|
||||
if (stype == PrimitiveType.ARRAY) {
|
||||
return translate(clsGen, type.getArrayElement()) + "[]";
|
||||
}
|
||||
return stype.getLongName();
|
||||
}
|
||||
|
||||
public static String signature(ArgType type) {
|
||||
final PrimitiveType stype = type.getPrimitiveType();
|
||||
PrimitiveType stype = type.getPrimitiveType();
|
||||
if (stype == PrimitiveType.OBJECT) {
|
||||
return Utils.makeQualifiedObjectName(type.getObject());
|
||||
}
|
||||
@@ -80,37 +66,91 @@ public class TypeGen {
|
||||
}
|
||||
|
||||
public static String formatShort(short s) {
|
||||
return "(short) " + wrapNegNum(s < 0, Short.toString(s));
|
||||
if (s == Short.MAX_VALUE) {
|
||||
return "Short.MAX_VALUE";
|
||||
}
|
||||
if (s == Short.MIN_VALUE) {
|
||||
return "Short.MIN_VALUE";
|
||||
}
|
||||
return "(short) " + Short.toString(s);
|
||||
}
|
||||
|
||||
public static String formatByte(byte b) {
|
||||
return "(byte) " + wrapNegNum(b < 0, Byte.toString(b));
|
||||
if (b == Byte.MAX_VALUE) {
|
||||
return "Byte.MAX_VALUE";
|
||||
}
|
||||
if (b == Byte.MIN_VALUE) {
|
||||
return "Byte.MIN_VALUE";
|
||||
}
|
||||
return "(byte) " + Byte.toString(b);
|
||||
}
|
||||
|
||||
public static String formatInteger(int i) {
|
||||
return wrapNegNum(i < 0, Integer.toString(i));
|
||||
if (i == Integer.MAX_VALUE) {
|
||||
return "Integer.MAX_VALUE";
|
||||
}
|
||||
if (i == Integer.MIN_VALUE) {
|
||||
return "Integer.MIN_VALUE";
|
||||
}
|
||||
return Integer.toString(i);
|
||||
}
|
||||
|
||||
public static String formatLong(long l) {
|
||||
if (l == Long.MAX_VALUE) {
|
||||
return "Long.MAX_VALUE";
|
||||
}
|
||||
if (l == Long.MIN_VALUE) {
|
||||
return "Long.MIN_VALUE";
|
||||
}
|
||||
String str = Long.toString(l);
|
||||
if (Math.abs(l) >= Integer.MAX_VALUE) {
|
||||
str += "L";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String formatDouble(double d) {
|
||||
return wrapNegNum(d < 0, Double.toString(d) + "d");
|
||||
if (Double.isNaN(d)) {
|
||||
return "Double.NaN";
|
||||
}
|
||||
if (d == Double.NEGATIVE_INFINITY) {
|
||||
return "Double.NEGATIVE_INFINITY";
|
||||
}
|
||||
if (d == Double.POSITIVE_INFINITY) {
|
||||
return "Double.POSITIVE_INFINITY";
|
||||
}
|
||||
if (d == Double.MIN_VALUE) {
|
||||
return "Double.MIN_VALUE";
|
||||
}
|
||||
if (d == Double.MAX_VALUE) {
|
||||
return "Double.MAX_VALUE";
|
||||
}
|
||||
if (d == Double.MIN_NORMAL) {
|
||||
return "Double.MIN_NORMAL";
|
||||
}
|
||||
return Double.toString(d) + "d";
|
||||
}
|
||||
|
||||
public static String formatFloat(float f) {
|
||||
return wrapNegNum(f < 0, Float.toString(f) + "f");
|
||||
}
|
||||
|
||||
public static String formatLong(long lit) {
|
||||
String l = Long.toString(lit);
|
||||
if (lit == Long.MIN_VALUE || Math.abs(lit) >= Integer.MAX_VALUE) {
|
||||
l += "L";
|
||||
if (Float.isNaN(f)) {
|
||||
return "Float.NaN";
|
||||
}
|
||||
return wrapNegNum(lit < 0, l);
|
||||
if (f == Float.NEGATIVE_INFINITY) {
|
||||
return "Float.NEGATIVE_INFINITY";
|
||||
}
|
||||
if (f == Float.POSITIVE_INFINITY) {
|
||||
return "Float.POSITIVE_INFINITY";
|
||||
}
|
||||
if (f == Float.MIN_VALUE) {
|
||||
return "Float.MIN_VALUE";
|
||||
}
|
||||
if (f == Float.MAX_VALUE) {
|
||||
return "Float.MAX_VALUE";
|
||||
}
|
||||
if (f == Float.MIN_NORMAL) {
|
||||
return "Float.MIN_NORMAL";
|
||||
}
|
||||
return Float.toString(f) + "f";
|
||||
}
|
||||
|
||||
private static String wrapNegNum(boolean lz, String str) {
|
||||
// if (lz)
|
||||
// return "(" + str + ")";
|
||||
// else
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
public enum AttributeFlag {
|
||||
public enum AFlag {
|
||||
TRY_ENTER,
|
||||
TRY_LEAVE,
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.dex.attributes.nodes.MethodInlineAttr;
|
||||
import jadx.core.dex.attributes.nodes.PhiListAttr;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.nodes.parser.FieldValueAttr;
|
||||
import jadx.core.dex.trycatch.CatchAttr;
|
||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||
import jadx.core.dex.trycatch.SplitterBlockAttr;
|
||||
|
||||
/**
|
||||
* Attribute types enumeration,
|
||||
* uses generic type for omit cast after in 'AttributeStorage.get' method
|
||||
*
|
||||
* @param <T> attribute class implementation
|
||||
*/
|
||||
public class AType<T extends IAttribute> {
|
||||
|
||||
private AType() {
|
||||
}
|
||||
|
||||
public static final AType<AttrList<JumpInfo>> JUMP = new AType<AttrList<JumpInfo>>();
|
||||
public static final AType<AttrList<LoopInfo>> LOOP = new AType<AttrList<LoopInfo>>();
|
||||
|
||||
public static final AType<ExcHandlerAttr> EXC_HANDLER = new AType<ExcHandlerAttr>();
|
||||
public static final AType<CatchAttr> CATCH_BLOCK = new AType<CatchAttr>();
|
||||
public static final AType<SplitterBlockAttr> SPLITTER_BLOCK = new AType<SplitterBlockAttr>();
|
||||
public static final AType<ForceReturnAttr> FORCE_RETURN = new AType<ForceReturnAttr>();
|
||||
public static final AType<FieldValueAttr> FIELD_VALUE = new AType<FieldValueAttr>();
|
||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<FieldReplaceAttr>();
|
||||
public static final AType<JadxErrorAttr> JADX_ERROR = new AType<JadxErrorAttr>();
|
||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<MethodInlineAttr>();
|
||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<EnumClassAttr>();
|
||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<AnnotationsList>();
|
||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<MethodParameters>();
|
||||
public static final AType<PhiListAttr> PHI_LIST = new AType<PhiListAttr>();
|
||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<SourceFileAttr>();
|
||||
public static final AType<DeclareVariablesAttr> DECLARE_VARIABLES = new AType<DeclareVariablesAttr>();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class AttrList<T> implements IAttribute {
|
||||
|
||||
private final AType<AttrList<T>> type;
|
||||
private final List<T> list = new LinkedList<T>();
|
||||
|
||||
public AttrList(AType<AttrList<T>> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public List<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AType<AttrList<T>> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.listToString(list);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,96 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AttrNode implements IAttributeNode {
|
||||
|
||||
private AttributesList attributesList;
|
||||
private static final AttributeStorage EMPTY_ATTR_STORAGE = new EmptyAttrStorage();
|
||||
|
||||
private AttributeStorage storage = EMPTY_ATTR_STORAGE;
|
||||
|
||||
@Override
|
||||
public AttributesList getAttributes() {
|
||||
if (attributesList == null) {
|
||||
attributesList = new AttributesList();
|
||||
}
|
||||
return attributesList;
|
||||
public void add(AFlag flag) {
|
||||
getStorage().add(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAttr(IAttribute attr) {
|
||||
getStorage().add(attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void addAttr(AType<AttrList<T>> type, T obj) {
|
||||
getStorage().add(type, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyAttributesFrom(AttrNode attrNode) {
|
||||
getStorage().addAll(attrNode.storage);
|
||||
}
|
||||
|
||||
AttributeStorage getStorage() {
|
||||
AttributeStorage store = storage;
|
||||
if (store == EMPTY_ATTR_STORAGE) {
|
||||
store = new AttributeStorage();
|
||||
storage = store;
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AFlag flag) {
|
||||
return storage.contains(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAttribute> boolean contains(AType<T> type) {
|
||||
return storage.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAttribute> T get(AType<T> type) {
|
||||
return storage.get(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation(String cls) {
|
||||
return storage.getAnnotation(cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAll(AType<AttrList<T>> type) {
|
||||
return storage.getAll(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(AFlag flag) {
|
||||
storage.remove(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAttribute> void remove(AType<T> type) {
|
||||
storage.remove(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttr(IAttribute attr) {
|
||||
storage.remove(attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAttributes() {
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAttributesStringsList() {
|
||||
return storage.getAttributeStrings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttributesString() {
|
||||
return storage.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Storage for different attribute types:
|
||||
* 1. flags - boolean attribute (set or not)
|
||||
* 2. attribute - class instance associated with attribute type.
|
||||
*/
|
||||
public class AttributeStorage {
|
||||
|
||||
private final Set<AFlag> flags;
|
||||
private final Map<AType<?>, IAttribute> attributes;
|
||||
|
||||
public AttributeStorage() {
|
||||
flags = EnumSet.noneOf(AFlag.class);
|
||||
attributes = new HashMap<AType<?>, IAttribute>(2);
|
||||
}
|
||||
|
||||
public void add(AFlag flag) {
|
||||
flags.add(flag);
|
||||
}
|
||||
|
||||
public void add(IAttribute attr) {
|
||||
attributes.put(attr.getType(), attr);
|
||||
}
|
||||
|
||||
public <T> void add(AType<AttrList<T>> type, T obj) {
|
||||
AttrList<T> list = get(type);
|
||||
if (list == null) {
|
||||
list = new AttrList<T>(type);
|
||||
add(list);
|
||||
}
|
||||
list.getList().add(obj);
|
||||
}
|
||||
|
||||
public void addAll(AttributeStorage otherList) {
|
||||
flags.addAll(otherList.flags);
|
||||
attributes.putAll(otherList.attributes);
|
||||
}
|
||||
|
||||
public boolean contains(AFlag flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
||||
public <T extends IAttribute> boolean contains(AType<T> type) {
|
||||
return attributes.containsKey(type);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends IAttribute> T get(AType<T> type) {
|
||||
return (T) attributes.get(type);
|
||||
}
|
||||
|
||||
public Annotation getAnnotation(String cls) {
|
||||
AnnotationsList aList = get(AType.ANNOTATION_LIST);
|
||||
return aList == null ? null : aList.get(cls);
|
||||
}
|
||||
|
||||
public <T> List<T> getAll(AType<AttrList<T>> type) {
|
||||
AttrList<T> attrList = get(type);
|
||||
if (attrList == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return attrList.getList();
|
||||
}
|
||||
|
||||
public void remove(AFlag flag) {
|
||||
flags.remove(flag);
|
||||
}
|
||||
|
||||
public <T extends IAttribute> void remove(AType<T> type) {
|
||||
attributes.remove(type);
|
||||
}
|
||||
|
||||
public void remove(IAttribute attr) {
|
||||
AType<?> type = attr.getType();
|
||||
IAttribute a = attributes.get(type);
|
||||
if (a == attr) {
|
||||
attributes.remove(type);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
flags.clear();
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
public List<String> getAttributeStrings() {
|
||||
int size = flags.size() + attributes.size() + attributes.size();
|
||||
if (size == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> list = new ArrayList<String>(size);
|
||||
for (AFlag a : flags) {
|
||||
list.add(a.toString());
|
||||
}
|
||||
for (IAttribute a : attributes.values()) {
|
||||
list.add(a.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> list = getAttributeStrings();
|
||||
if (list.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "A:{" + Utils.listToString(list) + "}";
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
public enum AttributeType {
|
||||
|
||||
/* Multi attributes */
|
||||
|
||||
JUMP(false),
|
||||
|
||||
LOOP(false),
|
||||
CATCH_BLOCK(false),
|
||||
|
||||
/* Uniq attributes */
|
||||
|
||||
EXC_HANDLER(true),
|
||||
SPLITTER_BLOCK(true),
|
||||
FORCE_RETURN(true),
|
||||
|
||||
FIELD_VALUE(true),
|
||||
|
||||
JADX_ERROR(true),
|
||||
METHOD_INLINE(true),
|
||||
FIELD_REPLACE(true),
|
||||
|
||||
ENUM_CLASS(true),
|
||||
|
||||
ANNOTATION_LIST(true),
|
||||
ANNOTATION_MTH_PARAMETERS(true),
|
||||
|
||||
SOURCE_FILE(true),
|
||||
|
||||
// for regions
|
||||
DECLARE_VARIABLES(true);
|
||||
|
||||
private static final int NOT_UNIQ_COUNT;
|
||||
private final boolean uniq;
|
||||
|
||||
private AttributeType(boolean isUniq) {
|
||||
this.uniq = isUniq;
|
||||
}
|
||||
|
||||
static {
|
||||
// place all not unique attributes at first
|
||||
int last = -1;
|
||||
AttributeType[] vals = AttributeType.values();
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
AttributeType type = vals[i];
|
||||
if (type.notUniq()) {
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
NOT_UNIQ_COUNT = last + 1;
|
||||
}
|
||||
|
||||
public static int getNotUniqCount() {
|
||||
return NOT_UNIQ_COUNT;
|
||||
}
|
||||
|
||||
public boolean isUniq() {
|
||||
return uniq;
|
||||
}
|
||||
|
||||
public boolean notUniq() {
|
||||
return !uniq;
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Storage for different attribute types:
|
||||
* 1. flags - boolean attribute (set or not)
|
||||
* 2. attribute - class instance associated for attribute type,
|
||||
* only one attached to node for unique attributes, multiple for others
|
||||
*/
|
||||
public final class AttributesList {
|
||||
|
||||
private final Set<AttributeFlag> flags;
|
||||
private final Map<AttributeType, IAttribute> uniqAttr;
|
||||
private final List<IAttribute> attributes;
|
||||
private final int[] attrCount;
|
||||
|
||||
public AttributesList() {
|
||||
flags = EnumSet.noneOf(AttributeFlag.class);
|
||||
uniqAttr = new EnumMap<AttributeType, IAttribute>(AttributeType.class);
|
||||
attributes = new LinkedList<IAttribute>();
|
||||
attrCount = new int[AttributeType.getNotUniqCount()];
|
||||
}
|
||||
|
||||
// Flags
|
||||
|
||||
public void add(AttributeFlag flag) {
|
||||
flags.add(flag);
|
||||
}
|
||||
|
||||
public boolean contains(AttributeFlag flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
||||
public void remove(AttributeFlag flag) {
|
||||
flags.remove(flag);
|
||||
}
|
||||
|
||||
// Attributes
|
||||
|
||||
public void add(IAttribute attr) {
|
||||
if (attr.getType().isUniq()) {
|
||||
uniqAttr.put(attr.getType(), attr);
|
||||
} else {
|
||||
addMultiAttribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMultiAttribute(IAttribute attr) {
|
||||
attributes.add(attr);
|
||||
attrCount[attr.getType().ordinal()]++;
|
||||
}
|
||||
|
||||
private int getMultiCountInternal(AttributeType type) {
|
||||
return attrCount[type.ordinal()];
|
||||
}
|
||||
|
||||
public void addAll(AttributesList otherList) {
|
||||
flags.addAll(otherList.flags);
|
||||
uniqAttr.putAll(otherList.uniqAttr);
|
||||
for (IAttribute attr : otherList.attributes) {
|
||||
addMultiAttribute(attr);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(AttributeType type) {
|
||||
if (type.isUniq()) {
|
||||
return uniqAttr.containsKey(type);
|
||||
} else {
|
||||
return getMultiCountInternal(type) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public IAttribute get(AttributeType type) {
|
||||
if (type.isUniq()) {
|
||||
return uniqAttr.get(type);
|
||||
} else {
|
||||
if (getMultiCountInternal(type) != 0) {
|
||||
for (IAttribute attr : attributes) {
|
||||
if (attr.getType() == type) {
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount(AttributeType type) {
|
||||
if (type.isUniq()) {
|
||||
return uniqAttr.containsKey(type) ? 1 : 0;
|
||||
} else {
|
||||
return getMultiCountInternal(type);
|
||||
}
|
||||
}
|
||||
|
||||
public Annotation getAnnotation(String cls) {
|
||||
AnnotationsList aList = (AnnotationsList) get(AttributeType.ANNOTATION_LIST);
|
||||
return aList == null ? null : aList.get(cls);
|
||||
}
|
||||
|
||||
public List<IAttribute> getAll(AttributeType type) {
|
||||
assert type.notUniq();
|
||||
|
||||
int count = getMultiCountInternal(type);
|
||||
if (count == 0) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
List<IAttribute> attrs = new ArrayList<IAttribute>(count);
|
||||
for (IAttribute attr : attributes) {
|
||||
if (attr.getType() == type) {
|
||||
attrs.add(attr);
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(AttributeType type) {
|
||||
if (type.isUniq()) {
|
||||
uniqAttr.remove(type);
|
||||
} else {
|
||||
for (Iterator<IAttribute> it = attributes.iterator(); it.hasNext(); ) {
|
||||
IAttribute attr = it.next();
|
||||
if (attr.getType() == type) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
attrCount[type.ordinal()] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(IAttribute attr) {
|
||||
AttributeType type = attr.getType();
|
||||
if (type.isUniq()) {
|
||||
IAttribute a = uniqAttr.get(type);
|
||||
if (a == attr) {
|
||||
uniqAttr.remove(type);
|
||||
}
|
||||
} else {
|
||||
if (getMultiCountInternal(type) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Iterator<IAttribute> it = attributes.iterator(); it.hasNext(); ) {
|
||||
IAttribute a = it.next();
|
||||
if (a == attr) {
|
||||
it.remove();
|
||||
attrCount[type.ordinal()]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
flags.clear();
|
||||
uniqAttr.clear();
|
||||
attributes.clear();
|
||||
Arrays.fill(attrCount, 0);
|
||||
}
|
||||
|
||||
public List<String> getAttributeStrings() {
|
||||
int size = flags.size() + uniqAttr.size() + attributes.size();
|
||||
if (size == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> list = new ArrayList<String>(size);
|
||||
for (AttributeFlag a : flags) {
|
||||
list.add(a.toString());
|
||||
}
|
||||
for (IAttribute a : uniqAttr.values()) {
|
||||
list.add(a.toString());
|
||||
}
|
||||
for (IAttribute a : attributes) {
|
||||
list.add(a.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> list = getAttributeStrings();
|
||||
if (list.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "A:{" + Utils.listToString(list) + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.TypedVar;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public final class BlockRegState {
|
||||
|
||||
private final RegisterArg[] regs;
|
||||
|
||||
public BlockRegState(MethodNode mth) {
|
||||
this.regs = new RegisterArg[mth.getRegsCount()];
|
||||
for (int i = 0; i < regs.length; i++) {
|
||||
regs[i] = new RegisterArg(i);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockRegState(BlockRegState state) {
|
||||
this.regs = new RegisterArg[state.regs.length];
|
||||
System.arraycopy(state.regs, 0, regs, 0, state.regs.length);
|
||||
}
|
||||
|
||||
public void assignReg(RegisterArg arg) {
|
||||
regs[arg.getRegNum()] = arg;
|
||||
arg.getTypedVar().getUseList().add(arg);
|
||||
}
|
||||
|
||||
public void use(RegisterArg arg) {
|
||||
RegisterArg reg = regs[arg.getRegNum()];
|
||||
TypedVar regType = reg.getTypedVar();
|
||||
if (regType == null) {
|
||||
regType = new TypedVar(arg.getType());
|
||||
reg.forceSetTypedVar(regType);
|
||||
}
|
||||
arg.replaceTypedVar(reg);
|
||||
reg.getTypedVar().getUseList().add(arg);
|
||||
}
|
||||
|
||||
public RegisterArg getRegister(int r) {
|
||||
return regs[r];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (RegisterArg reg : regs) {
|
||||
if (reg.getTypedVar() != null) {
|
||||
if (str.length() != 0) {
|
||||
str.append(", ");
|
||||
}
|
||||
str.append(reg);
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class EmptyAttrStorage extends AttributeStorage {
|
||||
|
||||
@Override
|
||||
public boolean contains(AFlag flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAttribute> boolean contains(AType<T> type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAttribute> T get(AType<T> type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation getAnnotation(String cls) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> getAll(AType<AttrList<T>> type) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(AFlag flag) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAttribute> void remove(AType<T> type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(IAttribute attr) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAttributeStrings() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,6 @@ package jadx.core.dex.attributes;
|
||||
|
||||
public interface IAttribute {
|
||||
|
||||
AttributeType getType();
|
||||
AType<?> getType();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,38 @@
|
||||
package jadx.core.dex.attributes;
|
||||
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IAttributeNode {
|
||||
|
||||
AttributesList getAttributes();
|
||||
void add(AFlag flag);
|
||||
|
||||
void addAttr(IAttribute attr);
|
||||
|
||||
<T> void addAttr(AType<AttrList<T>> type, T obj);
|
||||
|
||||
void copyAttributesFrom(AttrNode attrNode);
|
||||
|
||||
boolean contains(AFlag flag);
|
||||
|
||||
<T extends IAttribute> boolean contains(AType<T> type);
|
||||
|
||||
<T extends IAttribute> T get(AType<T> type);
|
||||
|
||||
Annotation getAnnotation(String cls);
|
||||
|
||||
<T> List<T> getAll(AType<AttrList<T>> type);
|
||||
|
||||
void remove(AFlag flag);
|
||||
|
||||
<T extends IAttribute> void remove(AType<T> type);
|
||||
|
||||
void removeAttr(IAttribute attr);
|
||||
|
||||
void clearAttributes();
|
||||
|
||||
List<String> getAttributesStringsList();
|
||||
|
||||
String getAttributesString();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.Map;
|
||||
|
||||
public class Annotation {
|
||||
|
||||
public static enum Visibility {
|
||||
public enum Visibility {
|
||||
BUILD, RUNTIME, SYSTEM
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.core.dex.attributes.annotations;
|
||||
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
@@ -33,8 +33,8 @@ public class AnnotationsList implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.ANNOTATION_LIST;
|
||||
public AType<AnnotationsList> getType() {
|
||||
return AType.ANNOTATION_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.core.dex.attributes.annotations;
|
||||
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
@@ -20,8 +20,8 @@ public class MethodParameters implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.ANNOTATION_MTH_PARAMETERS;
|
||||
public AType<MethodParameters> getType() {
|
||||
return AType.ANNOTATION_MTH_PARAMETERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
@@ -22,8 +24,8 @@ public class DeclareVariablesAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.DECLARE_VARIABLES;
|
||||
public AType<DeclareVariablesAttr> getType() {
|
||||
return AType.DECLARE_VARIABLES;
|
||||
}
|
||||
|
||||
@Override
|
||||
+6
-4
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
@@ -51,7 +53,7 @@ public class EnumClassAttr implements IAttribute {
|
||||
private MethodNode staticMethod;
|
||||
|
||||
public EnumClassAttr(int fieldsCount) {
|
||||
this.fields = new ArrayList<EnumClassAttr.EnumField>(fieldsCount);
|
||||
this.fields = new ArrayList<EnumField>(fieldsCount);
|
||||
}
|
||||
|
||||
public List<EnumField> getFields() {
|
||||
@@ -67,8 +69,8 @@ public class EnumClassAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.ENUM_CLASS;
|
||||
public AType<EnumClassAttr> getType() {
|
||||
return AType.ENUM_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
|
||||
public class FieldReplaceAttr implements IAttribute {
|
||||
@@ -21,8 +23,8 @@ public class FieldReplaceAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.FIELD_REPLACE;
|
||||
public AType<FieldReplaceAttr> getType() {
|
||||
return AType.FIELD_REPLACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
@@ -16,8 +18,8 @@ public class ForceReturnAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.FORCE_RETURN;
|
||||
public AType<ForceReturnAttr> getType() {
|
||||
return AType.FORCE_RETURN;
|
||||
}
|
||||
|
||||
@Override
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class JadxErrorAttr implements IAttribute {
|
||||
@@ -15,8 +17,8 @@ public class JadxErrorAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.JADX_ERROR;
|
||||
public AType<JadxErrorAttr> getType() {
|
||||
return AType.JADX_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
+9
-14
@@ -1,22 +1,17 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
public class JumpAttribute implements IAttribute {
|
||||
public class JumpInfo {
|
||||
|
||||
private final int src;
|
||||
private final int dest;
|
||||
|
||||
public JumpAttribute(int src, int dest) {
|
||||
public JumpInfo(int src, int dest) {
|
||||
this.src = src;
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.JUMP;
|
||||
}
|
||||
|
||||
public int getSrc() {
|
||||
return src;
|
||||
}
|
||||
@@ -25,11 +20,6 @@ public class JumpAttribute implements IAttribute {
|
||||
return dest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JUMP: " + InsnUtils.formatOffset(src) + " -> " + InsnUtils.formatOffset(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * dest + src;
|
||||
@@ -46,7 +36,12 @@ public class JumpAttribute implements IAttribute {
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
JumpAttribute other = (JumpAttribute) obj;
|
||||
JumpInfo other = (JumpInfo) obj;
|
||||
return dest == other.dest && src == other.src;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "JUMP: " + InsnUtils.formatOffset(src) + " -> " + InsnUtils.formatOffset(dest);
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
|
||||
public abstract class LineAttrNode extends AttrNode {
|
||||
|
||||
+6
-10
@@ -1,5 +1,6 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.Edge;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
@@ -10,13 +11,13 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class LoopAttr implements IAttribute {
|
||||
public class LoopInfo {
|
||||
|
||||
private final BlockNode start;
|
||||
private final BlockNode end;
|
||||
private final Set<BlockNode> loopBlocks;
|
||||
|
||||
public LoopAttr(BlockNode start, BlockNode end) {
|
||||
public LoopInfo(BlockNode start, BlockNode end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.loopBlocks = Collections.unmodifiableSet(BlockUtils.getAllPathsBlocks(start, end));
|
||||
@@ -30,11 +31,6 @@ public class LoopAttr implements IAttribute {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.LOOP;
|
||||
}
|
||||
|
||||
public Set<BlockNode> getLoopBlocks() {
|
||||
return loopBlocks;
|
||||
}
|
||||
@@ -49,7 +45,7 @@ public class LoopAttr implements IAttribute {
|
||||
for (BlockNode block : blocks) {
|
||||
// exit: successor node not from this loop, (don't change to getCleanSuccessors)
|
||||
for (BlockNode s : block.getSuccessors()) {
|
||||
if (!blocks.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
|
||||
if (!blocks.contains(s) && !s.contains(AType.EXC_HANDLER)) {
|
||||
nodes.add(block);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +61,7 @@ public class LoopAttr implements IAttribute {
|
||||
Set<BlockNode> blocks = getLoopBlocks();
|
||||
for (BlockNode block : blocks) {
|
||||
for (BlockNode s : block.getSuccessors()) {
|
||||
if (!blocks.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
|
||||
if (!blocks.contains(s) && !s.contains(AType.EXC_HANDLER)) {
|
||||
edges.add(new Edge(block, s));
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -1,5 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class MethodInlineAttr implements IAttribute {
|
||||
@@ -15,8 +17,8 @@ public class MethodInlineAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.METHOD_INLINE;
|
||||
public AType<MethodInlineAttr> getType() {
|
||||
return AType.METHOD_INLINE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,32 @@
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PhiListAttr implements IAttribute {
|
||||
|
||||
private final List<PhiInsn> list = new LinkedList<PhiInsn>();
|
||||
|
||||
@Override
|
||||
public AType<PhiListAttr> getType() {
|
||||
return AType.PHI_LIST;
|
||||
}
|
||||
|
||||
public List<PhiInsn> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("PHI: ");
|
||||
for (PhiInsn phiInsn : list) {
|
||||
sb.append('r').append(phiInsn.getResult().getRegNum()).append(" ");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -1,4 +1,7 @@
|
||||
package jadx.core.dex.attributes;
|
||||
package jadx.core.dex.attributes.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.IAttribute;
|
||||
|
||||
public class SourceFileAttr implements IAttribute {
|
||||
|
||||
@@ -13,8 +16,8 @@ public class SourceFileAttr implements IAttribute {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeType getType() {
|
||||
return AttributeType.SOURCE_FILE;
|
||||
public AType<SourceFileAttr> getType() {
|
||||
return AType.SOURCE_FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8,7 +8,7 @@ public class AccessInfo {
|
||||
|
||||
private final int accFlags;
|
||||
|
||||
public static enum AFType {
|
||||
public enum AFType {
|
||||
CLASS, FIELD, METHOD
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package jadx.core.dex.info;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
|
||||
import com.android.dx.io.FieldId;
|
||||
import com.android.dex.FieldId;
|
||||
|
||||
public class FieldInfo {
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.io.MethodId;
|
||||
import com.android.dx.io.ProtoId;
|
||||
import com.android.dex.MethodId;
|
||||
import com.android.dex.ProtoId;
|
||||
|
||||
public final class MethodInfo {
|
||||
|
||||
|
||||
@@ -61,6 +61,23 @@ public class ArithNode extends InsnNode {
|
||||
return op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ArithNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
ArithNode that = (ArithNode) obj;
|
||||
return op == that.op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + op.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": "
|
||||
|
||||
@@ -17,7 +17,7 @@ public enum ArithOp {
|
||||
|
||||
private final String symbol;
|
||||
|
||||
private ArithOp(String symbol) {
|
||||
ArithOp(String symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package jadx.core.dex.instructions;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class ConstClassNode extends InsnNode {
|
||||
public final class ConstClassNode extends InsnNode {
|
||||
|
||||
private final ArgType clsType;
|
||||
|
||||
@@ -16,6 +16,23 @@ public class ConstClassNode extends InsnNode {
|
||||
return clsType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ConstClassNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
ConstClassNode that = (ConstClassNode) obj;
|
||||
return clsType.equals(that.clsType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + clsType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + clsType;
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
|
||||
public class ConstStringNode extends InsnNode {
|
||||
public final class ConstStringNode extends InsnNode {
|
||||
|
||||
private final String str;
|
||||
|
||||
@@ -15,6 +15,23 @@ public class ConstStringNode extends InsnNode {
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ConstStringNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
ConstStringNode that = (ConstStringNode) obj;
|
||||
return str.equals(that.str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + str.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " \"" + str + "\"";
|
||||
|
||||
@@ -8,7 +8,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
|
||||
public class FillArrayNode extends InsnNode {
|
||||
public final class FillArrayNode extends InsnNode {
|
||||
|
||||
private final Object data;
|
||||
private ArgType elemType;
|
||||
@@ -53,4 +53,21 @@ public class FillArrayNode extends InsnNode {
|
||||
elemType = r;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof FillArrayNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
FillArrayNode that = (FillArrayNode) obj;
|
||||
return elemType.equals(that.elemType) && data == that.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + elemType.hashCode() + data.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,23 @@ public class GotoNode extends InsnNode {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof GotoNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
GotoNode gotoNode = (GotoNode) obj;
|
||||
return target == gotoNode.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "-> " + InsnUtils.formatOffset(target);
|
||||
|
||||
@@ -70,6 +70,23 @@ public class IfNode extends GotoNode {
|
||||
return elseBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof IfNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
IfNode ifNode = (IfNode) obj;
|
||||
return op == ifNode.op;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + op.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": "
|
||||
|
||||
@@ -12,7 +12,7 @@ public enum IfOp {
|
||||
|
||||
private final String symbol;
|
||||
|
||||
private IfOp(String symbol) {
|
||||
IfOp(String symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@@ -23,19 +23,19 @@ public enum IfOp {
|
||||
public IfOp invert() {
|
||||
switch (this) {
|
||||
case EQ:
|
||||
return IfOp.NE;
|
||||
return NE;
|
||||
case NE:
|
||||
return IfOp.EQ;
|
||||
return EQ;
|
||||
|
||||
case LT:
|
||||
return IfOp.GE;
|
||||
return GE;
|
||||
case LE:
|
||||
return IfOp.GT;
|
||||
return GT;
|
||||
|
||||
case GT:
|
||||
return IfOp.LE;
|
||||
return LE;
|
||||
case GE:
|
||||
return IfOp.LT;
|
||||
return LT;
|
||||
|
||||
default:
|
||||
throw new JadxRuntimeException("Unknown if operations type: " + this);
|
||||
|
||||
@@ -16,6 +16,23 @@ public class IndexInsnNode extends InsnNode {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof IndexInsnNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
IndexInsnNode that = (IndexInsnNode) obj;
|
||||
return index == null ? that.index == null : index.equals(that.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (index != null ? index.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + InsnUtils.indexToString(index);
|
||||
|
||||
@@ -12,29 +12,46 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import com.android.dx.io.Code;
|
||||
import java.io.EOFException;
|
||||
|
||||
import com.android.dex.Code;
|
||||
import com.android.dx.io.OpcodeInfo;
|
||||
import com.android.dx.io.Opcodes;
|
||||
import com.android.dx.io.instructions.DecodedInstruction;
|
||||
import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction;
|
||||
import com.android.dx.io.instructions.PackedSwitchPayloadDecodedInstruction;
|
||||
import com.android.dx.io.instructions.ShortArrayCodeInput;
|
||||
import com.android.dx.io.instructions.SparseSwitchPayloadDecodedInstruction;
|
||||
|
||||
public class InsnDecoder {
|
||||
|
||||
private final MethodNode method;
|
||||
private final DecodedInstruction[] insnArr;
|
||||
private final DexNode dex;
|
||||
private DecodedInstruction[] insnArr;
|
||||
|
||||
public InsnDecoder(MethodNode mthNode, Code mthCode) {
|
||||
public InsnDecoder(MethodNode mthNode) {
|
||||
this.method = mthNode;
|
||||
this.dex = method.dex();
|
||||
this.insnArr = DecodedInstruction.decodeAll(mthCode.getInstructions());
|
||||
}
|
||||
|
||||
public InsnNode[] run() throws DecodeException {
|
||||
InsnNode[] instructions = new InsnNode[insnArr.length];
|
||||
public void decodeInsns(Code mthCode) throws DecodeException {
|
||||
short[] encodedInstructions = mthCode.getInstructions();
|
||||
int size = encodedInstructions.length;
|
||||
DecodedInstruction[] decoded = new DecodedInstruction[size];
|
||||
ShortArrayCodeInput in = new ShortArrayCodeInput(encodedInstructions);
|
||||
|
||||
try {
|
||||
while (in.hasMore()) {
|
||||
decoded[in.cursor()] = DecodedInstruction.decode(in);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
throw new DecodeException(method, "", e);
|
||||
}
|
||||
insnArr = decoded;
|
||||
}
|
||||
|
||||
public InsnNode[] process() throws DecodeException {
|
||||
InsnNode[] instructions = new InsnNode[insnArr.length];
|
||||
for (int i = 0; i < insnArr.length; i++) {
|
||||
DecodedInstruction rawInsn = insnArr[i];
|
||||
if (rawInsn != null) {
|
||||
@@ -48,6 +65,7 @@ public class InsnDecoder {
|
||||
instructions[i] = null;
|
||||
}
|
||||
}
|
||||
insnArr = null;
|
||||
return instructions;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ public enum InsnType {
|
||||
|
||||
TERNARY,
|
||||
ARGS, // just generate arguments
|
||||
PHI,
|
||||
|
||||
NEW_MULTIDIM_ARRAY // TODO: now multidimensional arrays created using Array.newInstance function
|
||||
}
|
||||
|
||||
@@ -44,6 +44,26 @@ public class InvokeNode extends InsnNode {
|
||||
return mth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof InvokeNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
InvokeNode that = (InvokeNode) obj;
|
||||
return type == that.type && mth.equals(that.mth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + type.hashCode();
|
||||
result = 31 * result + mth.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": "
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class PhiInsn extends InsnNode {
|
||||
|
||||
public PhiInsn(int regNum, int predecessors) {
|
||||
super(InsnType.PHI, predecessors);
|
||||
setResult(InsnArg.reg(regNum, ArgType.UNKNOWN));
|
||||
for (int i = 0; i < predecessors; i++) {
|
||||
addReg(regNum, ArgType.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisterArg getArg(int n) {
|
||||
return (RegisterArg) super.getArg(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PHI: " + getResult() + " = " + Utils.listToString(getArguments());
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,29 @@ public class SwitchNode extends InsnNode {
|
||||
return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SwitchNode) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
SwitchNode that = (SwitchNode) obj;
|
||||
return def == that.def
|
||||
&& Arrays.equals(keys, that.keys)
|
||||
&& Arrays.equals(targets, that.targets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(keys);
|
||||
result = 31 * result + Arrays.hashCode(targets);
|
||||
result = 31 * result + def;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder targ = new StringBuilder();
|
||||
|
||||
@@ -48,6 +48,10 @@ public abstract class ArgType {
|
||||
ArgType.clsp = clsp;
|
||||
}
|
||||
|
||||
public static boolean isClspSet() {
|
||||
return ArgType.clsp != null;
|
||||
}
|
||||
|
||||
private static ArgType primitive(PrimitiveType stype) {
|
||||
return new PrimitiveArg(stype);
|
||||
}
|
||||
@@ -174,7 +178,7 @@ public abstract class ArgType {
|
||||
private final int bounds;
|
||||
|
||||
public WildcardType(ArgType obj, int bound) {
|
||||
super(ArgType.OBJECT.getObject());
|
||||
super(OBJECT.getObject());
|
||||
this.type = obj;
|
||||
this.bounds = bound;
|
||||
}
|
||||
@@ -214,7 +218,7 @@ public abstract class ArgType {
|
||||
if (bounds == 0) {
|
||||
return "?";
|
||||
}
|
||||
return "? " + (bounds == -1 ? "super" : "extends") + " " + type.toString();
|
||||
return "? " + (bounds == -1 ? "super" : "extends") + " " + type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +402,7 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see jadx.core.dex.instructions.args.ArgType.WildcardType#getWildcardBounds()
|
||||
* @see WildcardType#getWildcardBounds()
|
||||
*/
|
||||
public int getWildcardBounds() {
|
||||
return 0;
|
||||
@@ -437,7 +441,7 @@ public abstract class ArgType {
|
||||
}
|
||||
|
||||
public static ArgType merge(ArgType a, ArgType b) {
|
||||
if (b == null || a == null) {
|
||||
if (a == null || b == null) {
|
||||
return null;
|
||||
}
|
||||
if (a.equals(b)) {
|
||||
@@ -513,7 +517,7 @@ public abstract class ArgType {
|
||||
return OBJECT;
|
||||
} else {
|
||||
ArgType res = merge(ea, eb);
|
||||
return res == null ? null : ArgType.array(res);
|
||||
return res == null ? null : array(res);
|
||||
}
|
||||
} else if (b.equals(OBJECT)) {
|
||||
return OBJECT;
|
||||
|
||||
@@ -2,12 +2,16 @@ package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
|
||||
// TODO: don't extend RegisterArg (now used as a result of instruction)
|
||||
public final class FieldArg extends RegisterArg {
|
||||
|
||||
private final FieldInfo field;
|
||||
// instArg equal 'null' for static fields
|
||||
private final InsnArg instArg;
|
||||
|
||||
public FieldArg(FieldInfo field, int regNum) {
|
||||
super(regNum, field.getType());
|
||||
public FieldArg(FieldInfo field, InsnArg reg) {
|
||||
super(-1);
|
||||
this.instArg = reg;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@@ -15,8 +19,12 @@ public final class FieldArg extends RegisterArg {
|
||||
return field;
|
||||
}
|
||||
|
||||
public InsnArg getInstanceArg() {
|
||||
return instArg;
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return regNum == -1;
|
||||
return instArg == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,6 +37,37 @@ public final class FieldArg extends RegisterArg {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(ArgType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof FieldArg) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
FieldArg fieldArg = (FieldArg) obj;
|
||||
if (!field.equals(fieldArg.field)) {
|
||||
return false;
|
||||
}
|
||||
if (instArg != null ? !instArg.equals(fieldArg.instArg) : fieldArg.instArg != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + field.hashCode();
|
||||
result = 31 * result + (instArg != null ? instArg.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + field + ")";
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public class ImmutableTypedVar extends TypedVar {
|
||||
|
||||
public ImmutableTypedVar(ArgType type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceSetType(ArgType newType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean merge(TypedVar typedVar) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean merge(ArgType type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,8 @@ public abstract class InsnArg extends Typed {
|
||||
return reg(InsnUtils.getArg(insn, argNum), type);
|
||||
}
|
||||
|
||||
public static RegisterArg immutableReg(int regNum, ArgType type) {
|
||||
RegisterArg r = new RegisterArg(regNum);
|
||||
r.forceSetTypedVar(new ImmutableTypedVar(type));
|
||||
return r;
|
||||
public static MthParameterArg parameterReg(int regNum, ArgType type) {
|
||||
return new MthParameterArg(regNum, type);
|
||||
}
|
||||
|
||||
public static LiteralArg lit(long literal, ArgType type) {
|
||||
@@ -101,11 +99,11 @@ public abstract class InsnArg extends Typed {
|
||||
break;
|
||||
case CONST_STR:
|
||||
arg = wrap(insn);
|
||||
arg.getTypedVar().forceSetType(ArgType.STRING);
|
||||
arg.setType(ArgType.STRING);
|
||||
break;
|
||||
case CONST_CLASS:
|
||||
arg = wrap(insn);
|
||||
arg.getTypedVar().forceSetType(ArgType.CLASS);
|
||||
arg.setType(ArgType.CLASS);
|
||||
break;
|
||||
default:
|
||||
arg = wrap(insn);
|
||||
@@ -115,7 +113,7 @@ public abstract class InsnArg extends Typed {
|
||||
}
|
||||
|
||||
public boolean isThis() {
|
||||
// must be implemented in RegisterArg
|
||||
// must be implemented in RegisterArg and MthParameterArg
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public final class InsnWrapArg extends InsnArg {
|
||||
|
||||
public InsnWrapArg(InsnNode insn) {
|
||||
RegisterArg result = insn.getResult();
|
||||
this.typedVar = new TypedVar((result != null ? result.getType() : ArgType.VOID));
|
||||
this.type = result != null ? result.getType() : ArgType.VOID;
|
||||
this.wrappedInsn = insn;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,6 @@ public final class InsnWrapArg extends InsnArg {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(wrap: " + typedVar + "\n " + wrappedInsn + ")";
|
||||
return "(wrap: " + type + "\n " + wrappedInsn + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public final class LiteralArg extends InsnArg {
|
||||
}
|
||||
}
|
||||
this.literal = value;
|
||||
this.typedVar = new TypedVar(type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public long getLiteral() {
|
||||
@@ -37,7 +37,7 @@ public final class LiteralArg extends InsnArg {
|
||||
}
|
||||
|
||||
public boolean isInteger() {
|
||||
PrimitiveType type = typedVar.getType().getPrimitiveType();
|
||||
PrimitiveType type = this.type.getPrimitiveType();
|
||||
return (type == PrimitiveType.INT
|
||||
|| type == PrimitiveType.BYTE
|
||||
|| type == PrimitiveType.CHAR
|
||||
@@ -69,10 +69,10 @@ public final class LiteralArg extends InsnArg {
|
||||
if (getType().equals(ArgType.BOOLEAN) && (value.equals("true") || value.equals("false"))) {
|
||||
return value;
|
||||
}
|
||||
return "(" + value + " " + typedVar + ")";
|
||||
return "(" + value + " " + type + ")";
|
||||
} catch (JadxRuntimeException ex) {
|
||||
// can't convert literal to string
|
||||
return "(" + literal + " " + typedVar + ")";
|
||||
return "(" + literal + " " + type + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public class MthParameterArg extends RegisterArg {
|
||||
|
||||
private boolean isThis;
|
||||
|
||||
public MthParameterArg(int rn, ArgType type) {
|
||||
super(rn, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeImmutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(ArgType type) {
|
||||
}
|
||||
|
||||
public void markAsThis() {
|
||||
this.isThis = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isThis() {
|
||||
return isThis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (isThis) {
|
||||
return "this";
|
||||
}
|
||||
return super.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
void setSVar(SSAVar sVar) {
|
||||
if (isThis) {
|
||||
sVar.setName("this");
|
||||
}
|
||||
super.setSVar(sVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MthParameterArg)) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
MthParameterArg that = (MthParameterArg) obj;
|
||||
return isThis == that.isThis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (isThis ? 1 : 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public interface Named {
|
||||
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public final class NamedArg extends InsnArg {
|
||||
public final class NamedArg extends InsnArg implements Named {
|
||||
|
||||
private String name;
|
||||
|
||||
public NamedArg(String name, ArgType type) {
|
||||
this.name = name;
|
||||
this.typedVar = new TypedVar(type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -24,6 +24,6 @@ public final class NamedArg extends InsnArg {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + name + " " + typedVar + ")";
|
||||
return "(" + name + " " + type + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public enum PrimitiveType {
|
||||
private final String shortName;
|
||||
private final String longName;
|
||||
|
||||
private PrimitiveType(String shortName, String longName) {
|
||||
PrimitiveType(String shortName, String longName) {
|
||||
this.shortName = shortName;
|
||||
this.longName = longName;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.ConstClassNode;
|
||||
import jadx.core.dex.instructions.ConstStringNode;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
@@ -14,17 +15,18 @@ import jadx.core.dex.nodes.parser.FieldValueAttr;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RegisterArg extends InsnArg {
|
||||
public class RegisterArg extends InsnArg implements Named {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegisterArg.class);
|
||||
|
||||
protected final int regNum;
|
||||
protected SSAVar sVar;
|
||||
|
||||
public RegisterArg(int rn) {
|
||||
this.regNum = rn;
|
||||
}
|
||||
|
||||
public RegisterArg(int rn, ArgType type) {
|
||||
this.typedVar = new TypedVar(type);
|
||||
this.type = type;
|
||||
this.regNum = rn;
|
||||
}
|
||||
|
||||
@@ -37,18 +39,49 @@ public class RegisterArg extends InsnArg {
|
||||
return true;
|
||||
}
|
||||
|
||||
public InsnNode getAssignInsn() {
|
||||
for (InsnArg arg : getTypedVar().getUseList()) {
|
||||
InsnNode assignInsn = arg.getParentInsn();
|
||||
if (assignInsn == null) {
|
||||
// assign as function argument
|
||||
return null;
|
||||
} else if (assignInsn.getResult() != null
|
||||
&& assignInsn.getResult().getRegNum() == regNum) {
|
||||
return assignInsn;
|
||||
}
|
||||
public SSAVar getSVar() {
|
||||
return sVar;
|
||||
}
|
||||
|
||||
void setSVar(SSAVar sVar) {
|
||||
this.sVar = sVar;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (sVar == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return sVar.getName();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (sVar != null) {
|
||||
sVar.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNameEquals(InsnArg arg) {
|
||||
String n = getName();
|
||||
if (n == null || !(arg instanceof Named)) {
|
||||
return false;
|
||||
}
|
||||
return n.equals(((Named) arg).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(ArgType type) {
|
||||
if (sVar != null) {
|
||||
sVar.setType(type);
|
||||
}
|
||||
}
|
||||
|
||||
public void mergeDebugInfo(ArgType type, String name) {
|
||||
setType(type);
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public void forceType(ArgType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,54 +91,82 @@ public class RegisterArg extends InsnArg {
|
||||
*/
|
||||
public Object getConstValue(DexNode dex) {
|
||||
InsnNode parInsn = getAssignInsn();
|
||||
if (parInsn != null) {
|
||||
InsnType insnType = parInsn.getType();
|
||||
switch (insnType) {
|
||||
case CONST:
|
||||
return parInsn.getArg(0);
|
||||
case CONST_STR:
|
||||
return ((ConstStringNode) parInsn).getString();
|
||||
case CONST_CLASS:
|
||||
return ((ConstClassNode) parInsn).getClsType();
|
||||
case SGET:
|
||||
FieldInfo f = (FieldInfo) ((IndexInsnNode) parInsn).getIndex();
|
||||
FieldNode fieldNode = dex.resolveField(f);
|
||||
if (fieldNode != null) {
|
||||
FieldValueAttr attr = (FieldValueAttr) fieldNode.getAttributes().get(AttributeType.FIELD_VALUE);
|
||||
if (attr != null) {
|
||||
return attr.getValue();
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Field {} not found in dex {}", f, dex);
|
||||
if (parInsn == null) {
|
||||
return null;
|
||||
}
|
||||
InsnType insnType = parInsn.getType();
|
||||
switch (insnType) {
|
||||
case CONST:
|
||||
return parInsn.getArg(0);
|
||||
case CONST_STR:
|
||||
return ((ConstStringNode) parInsn).getString();
|
||||
case CONST_CLASS:
|
||||
return ((ConstClassNode) parInsn).getClsType();
|
||||
case SGET:
|
||||
FieldInfo f = (FieldInfo) ((IndexInsnNode) parInsn).getIndex();
|
||||
FieldNode fieldNode = dex.resolveField(f);
|
||||
if (fieldNode != null) {
|
||||
FieldValueAttr attr = fieldNode.get(AType.FIELD_VALUE);
|
||||
if (attr != null) {
|
||||
return attr.getValue();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Field {} not found in dex {}", f, dex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isThis() {
|
||||
if (isRegister()) {
|
||||
String name = getTypedVar().getName();
|
||||
if ("this".equals(name)) {
|
||||
return true;
|
||||
}
|
||||
// maybe it was moved from 'this' register
|
||||
InsnNode ai = getAssignInsn();
|
||||
if (ai != null && ai.getType() == InsnType.MOVE) {
|
||||
InsnArg arg = ai.getArg(0);
|
||||
if (arg != this && arg.isThis()) {
|
||||
return true;
|
||||
}
|
||||
if ("this".equals(getName())) {
|
||||
return true;
|
||||
}
|
||||
// maybe it was moved from 'this' register
|
||||
InsnNode ai = getAssignInsn();
|
||||
if (ai != null && ai.getType() == InsnType.MOVE) {
|
||||
InsnArg arg = ai.getArg(0);
|
||||
if (arg != this) {
|
||||
return arg.isThis();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public InsnNode getAssignInsn() {
|
||||
if (sVar == null) {
|
||||
return null;
|
||||
}
|
||||
RegisterArg assign = sVar.getAssign();
|
||||
if (assign != null) {
|
||||
return assign.getParentInsn();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public InsnNode getPhiAssignInsn() {
|
||||
PhiInsn usePhi = sVar.getUsedInPhi();
|
||||
if (usePhi != null) {
|
||||
return usePhi;
|
||||
}
|
||||
RegisterArg assign = sVar.getAssign();
|
||||
if (assign != null) {
|
||||
InsnNode parent = assign.getParentInsn();
|
||||
if (parent != null && parent.getType() == InsnType.PHI) {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean equalRegisterAndType(RegisterArg arg) {
|
||||
return regNum == arg.regNum && type.equals(arg.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return regNum * 31 + typedVar.hashCode();
|
||||
return (regNum * 31 + type.hashCode()) * 31 + (sVar != null ? sVar.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,15 +177,36 @@ public class RegisterArg extends InsnArg {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
if (!(obj instanceof RegisterArg)) {
|
||||
return false;
|
||||
}
|
||||
RegisterArg other = (RegisterArg) obj;
|
||||
return regNum == other.regNum && typedVar.equals(other.typedVar);
|
||||
if (regNum != other.regNum) {
|
||||
return false;
|
||||
}
|
||||
if (!type.equals(other.type)) {
|
||||
return false;
|
||||
}
|
||||
if (sVar != null && !sVar.equals(other.getSVar())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(r" + regNum + " " + typedVar + ")";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(r");
|
||||
sb.append(regNum);
|
||||
if (sVar != null) {
|
||||
sb.append("_").append(sVar.getVersion());
|
||||
}
|
||||
if (getName() != null) {
|
||||
sb.append(" '").append(getName()).append("'");
|
||||
}
|
||||
sb.append(" ");
|
||||
sb.append(type);
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SSAVar {
|
||||
|
||||
private final int regNum;
|
||||
private final int version;
|
||||
private VarName varName;
|
||||
|
||||
private RegisterArg assign;
|
||||
private final List<RegisterArg> useList = new ArrayList<RegisterArg>(2);
|
||||
private PhiInsn usedInPhi;
|
||||
|
||||
private ArgType type;
|
||||
|
||||
public SSAVar(int regNum, int v, RegisterArg assign) {
|
||||
this.regNum = regNum;
|
||||
this.version = v;
|
||||
this.assign = assign;
|
||||
|
||||
if (assign != null) {
|
||||
assign.setSVar(this);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRegNum() {
|
||||
return regNum;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public RegisterArg getAssign() {
|
||||
return assign;
|
||||
}
|
||||
|
||||
public void setAssign(RegisterArg assign) {
|
||||
this.assign = assign;
|
||||
}
|
||||
|
||||
public List<RegisterArg> getUseList() {
|
||||
return useList;
|
||||
}
|
||||
|
||||
public int getUseCount() {
|
||||
return useList.size();
|
||||
}
|
||||
|
||||
public void use(RegisterArg arg) {
|
||||
if (arg.getSVar() != null) {
|
||||
arg.getSVar().removeUse(arg);
|
||||
}
|
||||
arg.setSVar(this);
|
||||
useList.add(arg);
|
||||
}
|
||||
|
||||
public void removeUse(RegisterArg arg) {
|
||||
for (int i = 0, useListSize = useList.size(); i < useListSize; i++) {
|
||||
if (useList.get(i) == arg) {
|
||||
useList.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setUsedInPhi(PhiInsn usedInPhi) {
|
||||
this.usedInPhi = usedInPhi;
|
||||
}
|
||||
|
||||
public PhiInsn getUsedInPhi() {
|
||||
return usedInPhi;
|
||||
}
|
||||
|
||||
public boolean isUsedInPhi() {
|
||||
return usedInPhi != null;
|
||||
}
|
||||
|
||||
public int getVariableUseCount() {
|
||||
if (!isUsedInPhi()) {
|
||||
return useList.size();
|
||||
}
|
||||
return useList.size() + usedInPhi.getResult().getSVar().getUseCount();
|
||||
}
|
||||
|
||||
public ArgType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(ArgType type) {
|
||||
this.type = type;
|
||||
if (assign != null) {
|
||||
assign.type = type;
|
||||
}
|
||||
for (int i = 0, useListSize = useList.size(); i < useListSize; i++) {
|
||||
useList.get(i).type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (name != null) {
|
||||
if (varName == null) {
|
||||
varName = new VarName();
|
||||
}
|
||||
varName.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (varName == null) {
|
||||
return null;
|
||||
}
|
||||
return varName.getName();
|
||||
}
|
||||
|
||||
public VarName getVarName() {
|
||||
return varName;
|
||||
}
|
||||
|
||||
public void setVarName(VarName varName) {
|
||||
this.varName = varName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof SSAVar)) {
|
||||
return false;
|
||||
}
|
||||
SSAVar ssaVar = (SSAVar) o;
|
||||
return regNum == ssaVar.regNum && version == ssaVar.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * regNum + version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "r" + regNum + "_" + version;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,31 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Typed {
|
||||
|
||||
TypedVar typedVar;
|
||||
|
||||
public TypedVar getTypedVar() {
|
||||
return typedVar;
|
||||
}
|
||||
protected ArgType type;
|
||||
|
||||
public ArgType getType() {
|
||||
return typedVar.getType();
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean merge(Typed var) {
|
||||
return typedVar.merge(var.getTypedVar());
|
||||
public void setType(ArgType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean merge(ArgType var) {
|
||||
return typedVar.merge(var);
|
||||
public boolean isTypeImmutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void forceSetTypedVar(TypedVar arg) {
|
||||
this.typedVar = arg;
|
||||
}
|
||||
|
||||
public void mergeDebugInfo(Typed arg) {
|
||||
merge(arg);
|
||||
mergeName(arg);
|
||||
}
|
||||
|
||||
protected void mergeName(Typed arg) {
|
||||
getTypedVar().mergeName(arg.getTypedVar());
|
||||
}
|
||||
|
||||
public boolean replaceTypedVar(Typed var) {
|
||||
TypedVar curVar = this.typedVar;
|
||||
TypedVar newVar = var.typedVar;
|
||||
if (curVar == newVar) {
|
||||
return false;
|
||||
public boolean merge(ArgType newType) {
|
||||
ArgType m = ArgType.merge(type, newType);
|
||||
if (m != null && !m.equals(type)) {
|
||||
setType(m);
|
||||
return true;
|
||||
}
|
||||
if (curVar != null) {
|
||||
if (curVar.isImmutable()) {
|
||||
moveInternals(newVar, curVar);
|
||||
} else {
|
||||
newVar.merge(curVar);
|
||||
moveInternals(curVar, newVar);
|
||||
this.typedVar = newVar;
|
||||
}
|
||||
} else {
|
||||
this.typedVar = newVar;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void moveInternals(TypedVar from, TypedVar to) {
|
||||
List<InsnArg> curUseList = from.getUseList();
|
||||
if (curUseList.size() != 0) {
|
||||
for (InsnArg arg : curUseList) {
|
||||
if (arg != this) {
|
||||
arg.forceSetTypedVar(to);
|
||||
}
|
||||
}
|
||||
to.getUseList().addAll(curUseList);
|
||||
curUseList.clear();
|
||||
}
|
||||
to.mergeName(from);
|
||||
public boolean merge(InsnArg arg) {
|
||||
return merge(arg.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class TypedVar {
|
||||
|
||||
private ArgType type;
|
||||
private final List<InsnArg> useList = new ArrayList<InsnArg>(2);
|
||||
private String name;
|
||||
|
||||
public TypedVar(ArgType initType) {
|
||||
this.type = initType;
|
||||
}
|
||||
|
||||
public ArgType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be used very carefully
|
||||
*/
|
||||
public void forceSetType(ArgType newType) {
|
||||
type = newType;
|
||||
}
|
||||
|
||||
public boolean merge(TypedVar typedVar) {
|
||||
return merge(typedVar.getType());
|
||||
}
|
||||
|
||||
public boolean merge(ArgType mtype) {
|
||||
ArgType res = ArgType.merge(type, mtype);
|
||||
if (res != null && !type.equals(res)) {
|
||||
this.type = res;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<InsnArg> getUseList() {
|
||||
return useList;
|
||||
}
|
||||
|
||||
public void removeUse(InsnArg arg) {
|
||||
Iterator<InsnArg> it = useList.iterator();
|
||||
while (it.hasNext()) {
|
||||
InsnArg use = it.next();
|
||||
if (use == arg) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mergeName(TypedVar arg) {
|
||||
String argName = arg.getName();
|
||||
if (argName != null) {
|
||||
setName(argName);
|
||||
} else if (getName() != null) {
|
||||
arg.setName(getName());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isImmutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return type.hashCode() * 31 + (name == null ? 0 : name.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TypedVar)) {
|
||||
return false;
|
||||
}
|
||||
TypedVar other = (TypedVar) obj;
|
||||
if (!type.equals(other.type)) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (name != null) {
|
||||
sb.append('\'').append(name).append("' ");
|
||||
}
|
||||
sb.append(type);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
public class VarName {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ public class ConstructorInsn extends InsnNode {
|
||||
private final CallType callType;
|
||||
private final RegisterArg instanceArg;
|
||||
|
||||
private static enum CallType {
|
||||
private enum CallType {
|
||||
CONSTRUCTOR, // just new instance
|
||||
SUPER, // super call
|
||||
THIS, // call constructor from other constructor
|
||||
@@ -26,7 +26,6 @@ public class ConstructorInsn extends InsnNode {
|
||||
this.callMth = invoke.getCallMth();
|
||||
ClassInfo classType = callMth.getDeclClass();
|
||||
instanceArg = (RegisterArg) invoke.getArg(0);
|
||||
instanceArg.setParentInsn(this);
|
||||
|
||||
if (instanceArg.isThis()) {
|
||||
if (classType.equals(mth.getParentClass().getClassInfo())) {
|
||||
@@ -42,11 +41,15 @@ public class ConstructorInsn extends InsnNode {
|
||||
} else {
|
||||
callType = CallType.CONSTRUCTOR;
|
||||
setResult(instanceArg);
|
||||
// convert from 'use' to 'assign'
|
||||
instanceArg.getSVar().setAssign(instanceArg);
|
||||
}
|
||||
instanceArg.getSVar().removeUse(instanceArg);
|
||||
for (int i = 1; i < invoke.getArgsCount(); i++) {
|
||||
addArg(invoke.getArg(i));
|
||||
}
|
||||
offset = invoke.getOffset();
|
||||
setSourceLine(invoke.getSourceLine());
|
||||
}
|
||||
|
||||
public MethodInfo getCallMth() {
|
||||
@@ -73,6 +76,29 @@ public class ConstructorInsn extends InsnNode {
|
||||
return callType == CallType.SELF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ConstructorInsn) || !super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
ConstructorInsn that = (ConstructorInsn) o;
|
||||
return callMth.equals(that.callMth)
|
||||
&& callType == that.callType
|
||||
&& instanceArg.equals(that.instanceArg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + callMth.hashCode();
|
||||
result = 31 * result + callType.hashCode();
|
||||
result = 31 * result + instanceArg.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " " + callMth + " " + callType;
|
||||
|
||||
@@ -33,6 +33,23 @@ public class TernaryInsn extends InsnNode {
|
||||
return condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof TernaryInsn) || !super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
TernaryInsn that = (TernaryInsn) obj;
|
||||
return condition.equals(that.condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + condition.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return InsnUtils.formatOffset(offset) + ": TERNARY"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.BlockRegState;
|
||||
import jadx.core.dex.attributes.LoopAttr;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockNode extends AttrNode implements IBlock {
|
||||
@@ -29,10 +29,7 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
// immediate dominator
|
||||
private BlockNode idom;
|
||||
// blocks on which dominates this block
|
||||
private final List<BlockNode> dominatesOn = new ArrayList<BlockNode>(1);
|
||||
|
||||
private BlockRegState startState;
|
||||
private BlockRegState endState;
|
||||
private List<BlockNode> dominatesOn = Collections.emptyList();
|
||||
|
||||
public BlockNode(int id, int offset) {
|
||||
this.id = id;
|
||||
@@ -64,9 +61,17 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
cleanSuccessors = Collections.unmodifiableList(cleanSuccessors);
|
||||
successors = Collections.unmodifiableList(successors);
|
||||
predecessors = Collections.unmodifiableList(predecessors);
|
||||
cleanSuccessors = lockList(cleanSuccessors);
|
||||
successors = lockList(successors);
|
||||
predecessors = lockList(predecessors);
|
||||
dominatesOn = lockList(dominatesOn);
|
||||
}
|
||||
|
||||
List<BlockNode> lockList(List<BlockNode> list) {
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,26 +79,32 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
*/
|
||||
private static List<BlockNode> cleanSuccessors(BlockNode block) {
|
||||
List<BlockNode> sucList = block.getSuccessors();
|
||||
List<BlockNode> nodes = new ArrayList<BlockNode>(sucList.size());
|
||||
LoopAttr loop = (LoopAttr) block.getAttributes().get(AttributeType.LOOP);
|
||||
if (loop == null) {
|
||||
for (BlockNode b : sucList) {
|
||||
if (!b.getAttributes().contains(AttributeType.EXC_HANDLER)) {
|
||||
nodes.add(b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (BlockNode b : sucList) {
|
||||
if (!b.getAttributes().contains(AttributeType.EXC_HANDLER)) {
|
||||
// don't follow back edge
|
||||
if (loop.getStart() == b && loop.getEnd() == block) {
|
||||
continue;
|
||||
}
|
||||
nodes.add(b);
|
||||
if (sucList.isEmpty()) {
|
||||
return sucList;
|
||||
}
|
||||
List<BlockNode> toRemove = new LinkedList<BlockNode>();
|
||||
for (BlockNode b : sucList) {
|
||||
if (b.contains(AType.EXC_HANDLER)) {
|
||||
toRemove.add(b);
|
||||
} else if (b.contains(AFlag.SYNTHETIC)) {
|
||||
List<BlockNode> s = b.getSuccessors();
|
||||
if (s.size() == 1 && s.get(0).contains(AType.EXC_HANDLER)) {
|
||||
toRemove.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes.size() == sucList.size() ? sucList : nodes;
|
||||
if (block.contains(AFlag.LOOP_END)) {
|
||||
List<LoopInfo> loops = block.getAll(AType.LOOP);
|
||||
for (LoopInfo loop : loops) {
|
||||
toRemove.add(loop.getStart());
|
||||
}
|
||||
}
|
||||
if (toRemove.isEmpty()) {
|
||||
return sucList;
|
||||
}
|
||||
List<BlockNode> result = new ArrayList<BlockNode>(sucList);
|
||||
result.removeAll(toRemove);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,28 +157,19 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
return dominatesOn;
|
||||
}
|
||||
|
||||
public BlockRegState getStartState() {
|
||||
return startState;
|
||||
}
|
||||
|
||||
public void setStartState(BlockRegState startState) {
|
||||
this.startState = startState;
|
||||
}
|
||||
|
||||
public BlockRegState getEndState() {
|
||||
return endState;
|
||||
}
|
||||
|
||||
public void setEndState(BlockRegState endState) {
|
||||
this.endState = endState;
|
||||
public void addDominatesOn(BlockNode block) {
|
||||
if (dominatesOn.isEmpty()) {
|
||||
dominatesOn = new LinkedList<BlockNode>();
|
||||
}
|
||||
dominatesOn.add(block);
|
||||
}
|
||||
|
||||
public boolean isSynthetic() {
|
||||
return getAttributes().contains(AttributeFlag.SYNTHETIC);
|
||||
return contains(AFlag.SYNTHETIC);
|
||||
}
|
||||
|
||||
public boolean isReturnBlock() {
|
||||
return getAttributes().contains(AttributeFlag.RETURN);
|
||||
return contains(AFlag.RETURN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,6 +201,11 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String baseString() {
|
||||
return Integer.toString(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "B:" + id + ":" + InsnUtils.formatOffset(startOffset);
|
||||
|
||||
@@ -2,10 +2,11 @@ package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.SourceFileAttr;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.annotations.Annotation;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.AccessInfo.AFType;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -30,10 +31,10 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dx.io.ClassData;
|
||||
import com.android.dx.io.ClassData.Field;
|
||||
import com.android.dx.io.ClassData.Method;
|
||||
import com.android.dx.io.ClassDef;
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.ClassData.Field;
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.ClassDef;
|
||||
|
||||
public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
|
||||
@@ -103,15 +104,16 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
int sfIdx = cls.getSourceFileIndex();
|
||||
if (sfIdx != DexNode.NO_INDEX) {
|
||||
String fileName = dex.getString(sfIdx);
|
||||
if (!this.getFullName().contains(fileName.replace(".java", ""))) {
|
||||
this.getAttributes().add(new SourceFileAttr(fileName));
|
||||
if (!this.getFullName().contains(fileName.replace(".java", ""))
|
||||
&& !fileName.equals("SourceFile")) {
|
||||
this.addAttr(new SourceFileAttr(fileName));
|
||||
LOG.debug("Class '{}' compiled from '{}'", this, fileName);
|
||||
}
|
||||
}
|
||||
|
||||
// restore original access flags from dalvik annotation if present
|
||||
int accFlagsValue;
|
||||
Annotation a = getAttributes().getAnnotation(Consts.DALVIK_INNER_CLASS);
|
||||
Annotation a = getAnnotation(Consts.DALVIK_INNER_CLASS);
|
||||
if (a != null) {
|
||||
accFlagsValue = (Integer) a.getValues().get("accessFlags");
|
||||
} else {
|
||||
@@ -138,8 +140,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
private void loadStaticValues(ClassDef cls, List<FieldNode> staticFields) throws DecodeException {
|
||||
for (FieldNode f : staticFields) {
|
||||
if (f.getAccessFlags().isFinal()) {
|
||||
FieldValueAttr nullValue = new FieldValueAttr(null);
|
||||
f.getAttributes().add(nullValue);
|
||||
f.addAttr(new FieldValueAttr(null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +152,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
for (FieldNode f : staticFields) {
|
||||
AccessInfo accFlags = f.getAccessFlags();
|
||||
if (accFlags.isStatic() && accFlags.isFinal()) {
|
||||
FieldValueAttr fv = (FieldValueAttr) f.getAttributes().get(AttributeType.FIELD_VALUE);
|
||||
FieldValueAttr fv = f.get(AType.FIELD_VALUE);
|
||||
if (fv != null && fv.getValue() != null) {
|
||||
if (accFlags.isPublic()) {
|
||||
dex.getConstFields().put(fv.getValue(), f);
|
||||
@@ -204,9 +205,14 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws DecodeException {
|
||||
public void load() {
|
||||
for (MethodNode mth : getMethods()) {
|
||||
mth.load();
|
||||
try {
|
||||
mth.load();
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Method load error", e);
|
||||
mth.addAttr(new JadxErrorAttr(e));
|
||||
}
|
||||
}
|
||||
for (ClassNode innerCls : getInnerClasses()) {
|
||||
innerCls.load();
|
||||
|
||||
@@ -13,23 +13,23 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.android.dx.io.ClassData;
|
||||
import com.android.dx.io.ClassData.Method;
|
||||
import com.android.dx.io.ClassDef;
|
||||
import com.android.dx.io.Code;
|
||||
import com.android.dx.io.DexBuffer;
|
||||
import com.android.dx.io.DexBuffer.Section;
|
||||
import com.android.dx.io.FieldId;
|
||||
import com.android.dx.io.MethodId;
|
||||
import com.android.dx.io.ProtoId;
|
||||
import com.android.dx.merge.TypeList;
|
||||
import com.android.dex.ClassData;
|
||||
import com.android.dex.ClassData.Method;
|
||||
import com.android.dex.ClassDef;
|
||||
import com.android.dex.Code;
|
||||
import com.android.dex.Dex;
|
||||
import com.android.dex.Dex.Section;
|
||||
import com.android.dex.FieldId;
|
||||
import com.android.dex.MethodId;
|
||||
import com.android.dex.ProtoId;
|
||||
import com.android.dex.TypeList;
|
||||
|
||||
public class DexNode {
|
||||
|
||||
public static final int NO_INDEX = -1;
|
||||
|
||||
private final RootNode root;
|
||||
private final DexBuffer dexBuf;
|
||||
private final Dex dexBuf;
|
||||
private final List<ClassNode> classes = new ArrayList<ClassNode>();
|
||||
private final String[] strings;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.core.dex.attributes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.AccessInfo.AFType;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
|
||||
import com.android.dx.io.ClassData.Field;
|
||||
import com.android.dex.ClassData.Field;
|
||||
|
||||
public class FieldNode extends LineAttrNode {
|
||||
|
||||
|
||||
@@ -3,4 +3,7 @@ package jadx.core.dex.nodes;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
|
||||
public interface IContainer extends IAttributeNode {
|
||||
|
||||
// unique id for use in 'toString()' method
|
||||
String baseString();
|
||||
}
|
||||
|
||||
@@ -17,4 +17,13 @@ public class InsnContainer extends AttrNode implements IBlock {
|
||||
return insns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String baseString() {
|
||||
return Integer.toString(insns.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InsnContainer:" + insns.size();
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user