Merge branch 'master' into type-inference-wip
This commit is contained in:
+2
-1
@@ -9,7 +9,6 @@ git:
|
||||
depth: false
|
||||
|
||||
before_install:
|
||||
- wget https://github.com/sormuras/bach/raw/master/install-jdk.sh
|
||||
- chmod +x gradlew
|
||||
|
||||
# override install to skip 'gradle assemble'
|
||||
@@ -26,6 +25,8 @@ matrix:
|
||||
include:
|
||||
- env: JDK=oracle-8
|
||||
jdk: oraclejdk8
|
||||
- env: JDK=openjdk11
|
||||
jdk: openjdk11
|
||||
|
||||
script:
|
||||
- java -version
|
||||
|
||||
@@ -25,8 +25,13 @@ After download unpack zip file go to `bin` directory and run:
|
||||
On Windows run `.bat` files with double-click\
|
||||
**Note:** ensure you have installed Java 8 64-bit version
|
||||
|
||||
### Building from source
|
||||
Java 8 JDK or higher must be installed:
|
||||
|
||||
### Related projects:
|
||||
- [PyJadx](https://github.com/romainthomas/pyjadx) - python binding for jadx by [@romainthomas](https://github.com/romainthomas)
|
||||
|
||||
|
||||
### Building jadx from source
|
||||
JDK 8 or higher must be installed:
|
||||
|
||||
git clone https://github.com/skylot/jadx.git
|
||||
cd jadx
|
||||
|
||||
+3
-6
@@ -1,6 +1,5 @@
|
||||
plugins {
|
||||
id 'com.github.ksoichiro.console.reporter' version '0.5.0'
|
||||
id 'org.sonarqube' version '2.6.2'
|
||||
id 'org.sonarqube' version '2.7'
|
||||
id 'com.github.ben-manes.versions' version '0.20.0'
|
||||
}
|
||||
|
||||
@@ -12,7 +11,6 @@ allprojects {
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'groovy'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'com.github.ksoichiro.console.reporter'
|
||||
|
||||
version = jadxVersion
|
||||
|
||||
@@ -41,10 +39,9 @@ allprojects {
|
||||
|
||||
testCompile 'ch.qos.logback:logback-classic:1.2.3'
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.hamcrest:hamcrest-library:1.3'
|
||||
testCompile 'org.mockito:mockito-core:2.20.1'
|
||||
testCompile 'org.hamcrest:hamcrest-library:2.1'
|
||||
testCompile 'org.mockito:mockito-core:2.23.4'
|
||||
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
|
||||
testCompile 'cglib:cglib-nodep:3.2.7'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -5,13 +5,13 @@ dependencies {
|
||||
|
||||
compile files('lib/dx-1.16.jar')
|
||||
compile 'commons-io:commons-io:2.6'
|
||||
compile 'org.ow2.asm:asm:6.2'
|
||||
compile 'org.jetbrains:annotations:16.0.2'
|
||||
compile 'uk.com.robust-it:cloning:1.9.10'
|
||||
compile 'org.ow2.asm:asm:7.0'
|
||||
compile 'org.jetbrains:annotations:16.0.3'
|
||||
compile 'uk.com.robust-it:cloning:1.9.11'
|
||||
|
||||
testCompile 'org.smali:smali:2.2.4'
|
||||
testCompile 'org.smali:baksmali:2.2.4'
|
||||
testCompile 'org.smali:smali:2.2.5'
|
||||
testCompile 'org.smali:baksmali:2.2.5'
|
||||
|
||||
testCompile 'org.apache.commons:commons-lang3:3.7'
|
||||
testCompile 'org.apache.commons:commons-lang3:3.8.1'
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.utils.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -165,8 +167,16 @@ public class MethodGen {
|
||||
addFallbackMethodCode(code);
|
||||
code.startLine("*/");
|
||||
|
||||
ClassInfo clsAlias = mth.getParentClass().getAlias();
|
||||
|
||||
code.startLine("throw new UnsupportedOperationException(\"Method not decompiled: ")
|
||||
.add(mth.toString())
|
||||
.add(clsAlias.makeFullClsName(clsAlias.getShortName(), true))
|
||||
.add(".")
|
||||
.add(mth.getAlias())
|
||||
.add("(")
|
||||
.add(Utils.listToString(mth.getMethodInfo().getArgumentsTypes()))
|
||||
.add("):")
|
||||
.add(mth.getMethodInfo().getReturnType().toString())
|
||||
.add("\");");
|
||||
} else {
|
||||
RegionGen regionGen = new RegionGen(this);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package jadx.core.dex.instructions;
|
||||
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
|
||||
public interface CallMthInterface {
|
||||
|
||||
public MethodInfo getCallMth();
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
public class InvokeNode extends InsnNode {
|
||||
public class InvokeNode extends InsnNode implements CallMthInterface {
|
||||
|
||||
private final InvokeType type;
|
||||
private final MethodInfo mth;
|
||||
|
||||
@@ -2,13 +2,14 @@ package jadx.core.dex.instructions.mods;
|
||||
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.CallMthInterface;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
public class ConstructorInsn extends InsnNode {
|
||||
public class ConstructorInsn extends InsnNode implements CallMthInterface {
|
||||
|
||||
private final MethodInfo callMth;
|
||||
private final CallType callType;
|
||||
|
||||
@@ -4,19 +4,13 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.core.dex.instructions.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.instructions.ArithNode;
|
||||
import jadx.core.dex.instructions.ArithOp;
|
||||
import jadx.core.dex.instructions.ConstStringNode;
|
||||
import jadx.core.dex.instructions.IfNode;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.FieldArg;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -148,6 +142,15 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplify chains of calls to StringBuilder#append() plus constructor of StringBuilder.
|
||||
* Those chains are usually automatically generated by the Java compiler when you create String
|
||||
* concatenations like <code>"text " + 1 + " text"</code>.
|
||||
*
|
||||
* @param mth
|
||||
* @param insn
|
||||
* @return
|
||||
*/
|
||||
private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) {
|
||||
MethodInfo callMth = ((InvokeNode) insn).getCallMth();
|
||||
|
||||
@@ -202,7 +205,15 @@ public class SimplifyVisitor extends AbstractVisitor {
|
||||
}
|
||||
|
||||
for (; argInd < len; argInd++) { // Add the .append(xxx) arg string to concat
|
||||
concatInsn.addArg(chain.get(argInd).getArg(1));
|
||||
InsnNode node = chain.get(argInd);
|
||||
MethodInfo method = ((CallMthInterface) node).getCallMth();
|
||||
if (!(node.getArgsCount() < 2 && method.isConstructor() || method.getName().equals("append"))) {
|
||||
// The chain contains other calls to StringBuilder methods than the constructor or append.
|
||||
// We can't simplify such chains, therefore we leave them as they are.
|
||||
return null;
|
||||
}
|
||||
// process only constructor and append() calls
|
||||
concatInsn.addArg(node.getArg(1));
|
||||
}
|
||||
concatInsn.setResult(insn.getResult());
|
||||
return concatInsn;
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
package jadx.tests.api.utils;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.core.SubstringMatcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
|
||||
public class CountString extends SubstringMatcher {
|
||||
public class CountString extends TypeSafeMatcher<String> {
|
||||
|
||||
private final int count;
|
||||
private final String substring;
|
||||
|
||||
public CountString(int count, String substring) {
|
||||
super(substring);
|
||||
this.count = count;
|
||||
this.substring = substring;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean evalSubstringOf(String string) {
|
||||
return this.count == count(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String relationship() {
|
||||
return "containing <" + count + "> occurrence of";
|
||||
protected boolean matchesSafely(String item) {
|
||||
return this.count == count(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -27,7 +23,12 @@ public class CountString extends SubstringMatcher {
|
||||
mismatchDescription.appendText("found ").appendValue(count(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("containing <" + count + "> occurrence of ").appendValue(this.substring);
|
||||
}
|
||||
|
||||
private int count(String string) {
|
||||
return TestUtils.count(string, substring);
|
||||
return TestUtils.count(string, this.substring);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package jadx.tests.integration;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.visitors.SimplifyVisitor;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Test the StringBuilder simplification part of {@link SimplifyVisitor}
|
||||
*
|
||||
* @author Jan Peter Stotz
|
||||
*/
|
||||
public class SimplifyVisitorStringBuilderTest extends IntegrationTest {
|
||||
|
||||
public static class TestCls1 {
|
||||
public String test() {
|
||||
return new StringBuilder("[init]").append("a1").append('c').append(2).append(0l).append(1.0f).
|
||||
append(2.0d).append(true).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1() throws JadxException {
|
||||
ClassNode cls = getClassNode(SimplifyVisitorStringBuilderTest.TestCls1.class);
|
||||
SimplifyVisitor visitor = new SimplifyVisitor();
|
||||
visitor.visit(cls);
|
||||
String code = cls.getCode().toString();
|
||||
assertThat(code, containsString("return \"[init]\" + \"a1\" + 'c' + 2 + 0 + 1.0f + 2.0d + true;"));
|
||||
}
|
||||
|
||||
public static class TestCls2 {
|
||||
public String test() {
|
||||
// A chain with non-final variables
|
||||
String sInit = "[init]";
|
||||
String s = "a1";
|
||||
char c = 'c';
|
||||
int i = 1;
|
||||
long l = 2;
|
||||
float f = 1.0f;
|
||||
double d = 2.0d;
|
||||
boolean b = true;
|
||||
return new StringBuilder(sInit).append(s).append(c).append(i).append(l).append(f).
|
||||
append(d).append(b).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() throws JadxException {
|
||||
ClassNode cls = getClassNode(SimplifyVisitorStringBuilderTest.TestCls2.class);
|
||||
SimplifyVisitor visitor = new SimplifyVisitor();
|
||||
visitor.visit(cls);
|
||||
String code = cls.getCode().toString();
|
||||
assertThat(code, containsString("return \"[init]\" + \"a1\" + 'c' + 1 + 2 + 1.0f + 2.0d + true;"));
|
||||
}
|
||||
|
||||
public static class TestClsStringUtilsReverse {
|
||||
|
||||
/**
|
||||
* Simplified version of org.apache.commons.lang3.StringUtils.reverse()
|
||||
*/
|
||||
public static String reverse(final String str) {
|
||||
return new StringBuilder(str).reverse().toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3() throws JadxException {
|
||||
ClassNode cls = getClassNode(SimplifyVisitorStringBuilderTest.TestClsStringUtilsReverse.class);
|
||||
SimplifyVisitor visitor = new SimplifyVisitor();
|
||||
visitor.visit(cls);
|
||||
String code = cls.getCode().toString();
|
||||
assertThat(code, containsString("return new StringBuilder(str).reverse().toString();"));
|
||||
}
|
||||
|
||||
public static class TestClsChainWithDelete {
|
||||
public String test() {
|
||||
// a chain we can't simplify
|
||||
return new StringBuilder("[init]").append("a1").delete(1, 2).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChainWithDelete() throws JadxException {
|
||||
ClassNode cls = getClassNode(TestClsChainWithDelete.class);
|
||||
SimplifyVisitor visitor = new SimplifyVisitor();
|
||||
visitor.visit(cls);
|
||||
String code = cls.getCode().toString();
|
||||
assertThat(code, containsString("return new StringBuilder(\"[init]\").append(\"a1\").delete(1, 2).toString();"));
|
||||
}
|
||||
}
|
||||
@@ -10,15 +10,15 @@ mainClassName = 'jadx.gui.JadxGUI'
|
||||
dependencies {
|
||||
compile(project(":jadx-core"))
|
||||
compile(project(":jadx-cli"))
|
||||
compile 'com.fifesoft:rsyntaxtextarea:2.6.1'
|
||||
compile 'com.fifesoft:rsyntaxtextarea:3.0.0'
|
||||
compile 'com.google.code.gson:gson:2.8.5'
|
||||
compile files('libs/jfontchooser-1.0.5.jar')
|
||||
compile 'hu.kazocsaba:image-viewer:1.2.3'
|
||||
|
||||
compile 'org.apache.commons:commons-lang3:3.7'
|
||||
compile 'org.apache.commons:commons-lang3:3.8.1'
|
||||
|
||||
compile 'io.reactivex.rxjava2:rxjava:2.1.17'
|
||||
compile "com.github.akarnokd:rxjava2-swing:0.2.16"
|
||||
compile 'io.reactivex.rxjava2:rxjava:2.2.5'
|
||||
compile "com.github.akarnokd:rxjava2-swing:0.3.3"
|
||||
}
|
||||
|
||||
applicationDistribution.with {
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -64,10 +65,34 @@ public class JadxWrapper {
|
||||
new Thread(save).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the complete list of classes
|
||||
* @return
|
||||
*/
|
||||
public List<JavaClass> getClasses() {
|
||||
return decompiler.getClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all classes that are not excluded by the excluded packages settings
|
||||
* @return
|
||||
*/
|
||||
public List<JavaClass> getIncludedClasses() {
|
||||
List<JavaClass> classList = decompiler.getClasses();
|
||||
String excludedPackages = settings.getExcludedPackages().trim();
|
||||
if (excludedPackages.length() == 0)
|
||||
return classList;
|
||||
String[] excluded = excludedPackages.split("[ ]+");
|
||||
|
||||
return classList.stream().filter(cls -> {
|
||||
for (String exclude : excluded) {
|
||||
if (cls.getFullName().startsWith(exclude))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<JavaPackage> getPackages() {
|
||||
return decompiler.getPackages();
|
||||
}
|
||||
@@ -79,4 +104,8 @@ public class JadxWrapper {
|
||||
public File getOpenFile() {
|
||||
return openFile;
|
||||
}
|
||||
|
||||
public JadxSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public abstract class BackgroundJob {
|
||||
public Boolean call() throws Exception {
|
||||
runJob();
|
||||
executor.shutdown();
|
||||
return executor.awaitTermination(5, TimeUnit.MINUTES);
|
||||
return executor.awaitTermination(5, TimeUnit.DAYS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package jadx.gui.jobs;
|
||||
import javax.swing.*;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import jadx.gui.utils.NLS;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -62,6 +63,9 @@ public class BackgroundWorker extends SwingWorker<Void, Void> {
|
||||
if (searchIndex != null && searchIndex.getSkippedCount() > 0) {
|
||||
LOG.warn("Indexing of some classes skipped, count: {}, low memory: {}",
|
||||
searchIndex.getSkippedCount(), Utils.memoryInfo());
|
||||
String msg = NLS.str("message.indexingClassesSkipped");
|
||||
msg = String.format(msg, searchIndex.getSkippedCount());
|
||||
JOptionPane.showMessageDialog(null, msg);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Exception in background worker", e);
|
||||
|
||||
@@ -10,7 +10,7 @@ public class DecompileJob extends BackgroundJob {
|
||||
}
|
||||
|
||||
protected void runJob() {
|
||||
for (final JavaClass cls : wrapper.getClasses()) {
|
||||
for (final JavaClass cls : wrapper.getIncludedClasses()) {
|
||||
addTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -33,7 +33,7 @@ public class IndexJob extends BackgroundJob {
|
||||
final CodeUsageInfo usageInfo = new CodeUsageInfo(nodeCache);
|
||||
cache.setTextIndex(index);
|
||||
cache.setUsageInfo(usageInfo);
|
||||
for (final JavaClass cls : wrapper.getClasses()) {
|
||||
for (final JavaClass cls : wrapper.getIncludedClasses()) {
|
||||
addTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -42,6 +42,9 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
private String editorThemePath = "";
|
||||
private LangLocale langLocale = NLS.defaultLocale();
|
||||
private boolean autoStartJobs = false;
|
||||
protected String excludedPackages = "";
|
||||
|
||||
private boolean showHeapUsageBar = true;
|
||||
|
||||
private int settingsVersion = 0;
|
||||
|
||||
@@ -147,6 +150,24 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean isShowHeapUsageBar() {
|
||||
return showHeapUsageBar;
|
||||
}
|
||||
|
||||
public void setShowHeapUsageBar(boolean showHeapUsageBar) {
|
||||
this.showHeapUsageBar = showHeapUsageBar;
|
||||
partialSync(settings -> settings.showHeapUsageBar = showHeapUsageBar);
|
||||
}
|
||||
|
||||
public String getExcludedPackages() {
|
||||
return excludedPackages;
|
||||
}
|
||||
|
||||
public void setExcludedPackages(String excludedPackages) {
|
||||
this.excludedPackages = excludedPackages;
|
||||
}
|
||||
|
||||
public void setThreadsCount(int threadsCount) {
|
||||
this.threadsCount = threadsCount;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package jadx.gui.settings;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
@@ -242,6 +244,16 @@ public class JadxSettingsWindow extends JDialog {
|
||||
needReload();
|
||||
});
|
||||
|
||||
JButton editExcludedPackages = new JButton(NLS.str("preferences.excludedPackages.button"));
|
||||
editExcludedPackages.addActionListener( event -> {
|
||||
|
||||
String result = JOptionPane.showInputDialog(this, NLS.str("preferences.excludedPackages.editDialog"),
|
||||
settings.getExcludedPackages());
|
||||
if (result !=null) {
|
||||
settings.setExcludedPackages(result);
|
||||
}
|
||||
});
|
||||
|
||||
JCheckBox autoStartJobs = new JCheckBox();
|
||||
autoStartJobs.setSelected(settings.isAutoStartJobs());
|
||||
autoStartJobs.addItemListener(e -> settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED));
|
||||
@@ -269,6 +281,8 @@ public class JadxSettingsWindow extends JDialog {
|
||||
|
||||
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
|
||||
other.addRow(NLS.str("preferences.threads"), threadsCount);
|
||||
other.addRow(NLS.str("preferences.excludedPackages"), NLS.str("preferences.excludedPackages.tooltip"),
|
||||
editExcludedPackages);
|
||||
other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
|
||||
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
|
||||
other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode);
|
||||
@@ -334,6 +348,10 @@ public class JadxSettingsWindow extends JDialog {
|
||||
}
|
||||
|
||||
public void addRow(String label, JComponent comp) {
|
||||
addRow(label, null, comp);
|
||||
}
|
||||
|
||||
public void addRow(String label, String tooltip, JComponent comp) {
|
||||
c.gridy = row++;
|
||||
JLabel jLabel = new JLabel(label);
|
||||
jLabel.setLabelFor(comp);
|
||||
@@ -349,6 +367,12 @@ public class JadxSettingsWindow extends JDialog {
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.weightx = 0.2;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
|
||||
if (tooltip != null) {
|
||||
jLabel.setToolTipText(tooltip);
|
||||
comp.setToolTipText(tooltip);
|
||||
}
|
||||
|
||||
add(comp, c);
|
||||
|
||||
comp.addPropertyChangeListener("enabled", evt -> jLabel.setEnabled((boolean) evt.getNewValue()));
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package jadx.gui.ui;
|
||||
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class HeapUsageBar extends JProgressBar implements ActionListener {
|
||||
|
||||
private static final double TWO_TO_20 = 1048576d; // 1024 * 1024
|
||||
|
||||
private final Color GREEN = new Color(0, 180, 0);
|
||||
private final Color RED = new Color(200, 0, 0);
|
||||
|
||||
private final Runtime r;
|
||||
|
||||
private String maxHeapStr;
|
||||
|
||||
private final Timer timer;
|
||||
|
||||
private final double maxGB;
|
||||
|
||||
private final String textFormat;
|
||||
|
||||
public HeapUsageBar() {
|
||||
super();
|
||||
textFormat = NLS.str("heapUsage.text");
|
||||
r = Runtime.getRuntime();
|
||||
setBorderPainted(false);
|
||||
setStringPainted(true);
|
||||
setValue(10);
|
||||
int maxKB = (int) (r.maxMemory() / 1024);
|
||||
setMaximum(maxKB);
|
||||
maxGB = maxKB / TWO_TO_20;
|
||||
update();
|
||||
timer = new Timer(1000, this);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
long used = r.totalMemory() - r.freeMemory();
|
||||
int usedKB = (int) (used / 1024);
|
||||
setValue(usedKB);
|
||||
setString(String.format(textFormat, (usedKB / TWO_TO_20), maxGB));
|
||||
|
||||
if (used > r.totalMemory() * 0.8) {
|
||||
setForeground(RED);
|
||||
} else {
|
||||
setForeground(GREEN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean aFlag) {
|
||||
super.setVisible(aFlag);
|
||||
if (aFlag) {
|
||||
timer.start();
|
||||
} else {
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,10 +90,12 @@ public class MainWindow extends JFrame {
|
||||
private DefaultTreeModel treeModel;
|
||||
private JRoot treeRoot;
|
||||
private TabbedPane tabbedPane;
|
||||
private HeapUsageBar heapUsageBar;
|
||||
|
||||
private boolean isFlattenPackage;
|
||||
private JToggleButton flatPkgButton;
|
||||
private JCheckBoxMenuItem flatPkgMenuItem;
|
||||
private JCheckBoxMenuItem heapUsageBarMenuItem;
|
||||
|
||||
private JToggleButton deobfToggleBtn;
|
||||
private JCheckBoxMenuItem deobfMenuItem;
|
||||
@@ -124,6 +126,7 @@ public class MainWindow extends JFrame {
|
||||
public void open() {
|
||||
pack();
|
||||
setLocationAndPosition();
|
||||
heapUsageBar.setVisible(settings.isShowHeapUsageBar());
|
||||
setVisible(true);
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
@@ -185,7 +188,7 @@ public class MainWindow extends JFrame {
|
||||
protected void resetCache() {
|
||||
cacheObject.reset();
|
||||
// TODO: decompilation freezes sometime with several threads
|
||||
int threadsCount = 1; // settings.getThreadsCount();
|
||||
int threadsCount = settings.getThreadsCount();
|
||||
cacheObject.setDecompileJob(new DecompileJob(wrapper, threadsCount));
|
||||
cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount));
|
||||
}
|
||||
@@ -385,6 +388,13 @@ public class MainWindow extends JFrame {
|
||||
flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG);
|
||||
flatPkgMenuItem.setState(isFlattenPackage);
|
||||
|
||||
heapUsageBarMenuItem = new JCheckBoxMenuItem(NLS.str("menu.heapUsageBar"));
|
||||
heapUsageBarMenuItem.setState(settings.isShowHeapUsageBar());
|
||||
heapUsageBarMenuItem.addActionListener(event -> {
|
||||
settings.setShowHeapUsageBar(!settings.isShowHeapUsageBar());
|
||||
heapUsageBar.setVisible(settings.isShowHeapUsageBar());
|
||||
});
|
||||
|
||||
Action syncAction = new AbstractAction(NLS.str("menu.sync"), ICON_SYNC) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@@ -483,6 +493,7 @@ public class MainWindow extends JFrame {
|
||||
view.setMnemonic(KeyEvent.VK_V);
|
||||
view.add(flatPkgMenuItem);
|
||||
view.add(syncAction);
|
||||
view.add(heapUsageBarMenuItem);
|
||||
|
||||
JMenu nav = new JMenu(NLS.str("menu.navigation"));
|
||||
nav.setMnemonic(KeyEvent.VK_N);
|
||||
@@ -609,6 +620,9 @@ public class MainWindow extends JFrame {
|
||||
|
||||
new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this));
|
||||
|
||||
heapUsageBar = new HeapUsageBar();
|
||||
mainPanel.add(heapUsageBar, BorderLayout.SOUTH);
|
||||
|
||||
setContentPane(mainPanel);
|
||||
setTitle(DEFAULT_TITLE);
|
||||
}
|
||||
|
||||
@@ -180,6 +180,7 @@ public class SearchDialog extends CommonSearchDialog {
|
||||
.toList()
|
||||
.toFlowable(), 1)
|
||||
.observeOn(SwingSchedulers.edt())
|
||||
.doOnError(e -> LOG.error("Error while searching: {}", e.getMessage(), e))
|
||||
.subscribe(this::processSearchResults);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public class CodeIndex<T> implements SearchIndex<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(StringRef str, T value) {
|
||||
public synchronized void put(StringRef str, T value) {
|
||||
if (str == null || str.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,15 +69,17 @@ public class TextSearchIndex {
|
||||
int count = lines.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
StringRef line = lines.get(i);
|
||||
if (line.length() != 0 && line.charAt(0) != '}') {
|
||||
int lineNum = i + 1;
|
||||
JavaNode node = linesInfo.getJavaNodeByLine(lineNum);
|
||||
CodeNode codeNode = new CodeNode(nodeCache.makeFrom(node == null ? cls : node), lineNum, line);
|
||||
if (strRefSupported) {
|
||||
codeIndex.put(line, codeNode);
|
||||
} else {
|
||||
codeIndex.put(line.toString(), codeNode);
|
||||
}
|
||||
int lineLength = line.length();
|
||||
if (lineLength == 0 || (lineLength == 1 && line.charAt(0) == '}')) {
|
||||
continue;
|
||||
}
|
||||
int lineNum = i + 1;
|
||||
JavaNode node = linesInfo.getJavaNodeByLine(lineNum);
|
||||
CodeNode codeNode = new CodeNode(nodeCache.makeFrom(node == null ? cls : node), lineNum, line);
|
||||
if (strRefSupported) {
|
||||
codeIndex.put(line, codeNode);
|
||||
} else {
|
||||
codeIndex.put(line.toString(), codeNode);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -7,6 +7,7 @@ menu.no_recent_files=No recent files
|
||||
menu.preferences=Preferences
|
||||
menu.sync=Sync with editor
|
||||
menu.flatten=Show flatten packages
|
||||
menu.heapUsageBar=Show memory usage bar
|
||||
menu.navigation=Navigation
|
||||
menu.text_search=Text search
|
||||
menu.class_search=Class search
|
||||
@@ -46,6 +47,10 @@ tabs.closeAll=Close All
|
||||
nav.back=Back
|
||||
nav.forward=Forward
|
||||
|
||||
message.indexingClassesSkipped=<html>Jadx is running low on memory. Therefore %d classes were not indexed.<br>If you want all classes to be indexed restart Jadx with increased maximum heap size.</html>
|
||||
|
||||
heapUsage.text=JADX memory usage: %.2f GB of %.2f GB
|
||||
|
||||
search_dialog.open=Open
|
||||
search_dialog.cancel=Cancel
|
||||
search_dialog.open_by_name=Search for text:
|
||||
@@ -84,6 +89,10 @@ preferences.replaceConsts=Replace constants
|
||||
preferences.useImports=Use import statements
|
||||
preferences.skipResourcesDecode=Don't decode resources
|
||||
preferences.threads=Processing threads count
|
||||
preferences.excludedPackages=Excluded packages
|
||||
preferences.excludedPackages.tooltip=List of space separated package names that will not be decompiled or indexed (saves RAM)
|
||||
preferences.excludedPackages.button=Edit
|
||||
preferences.excludedPackages.editDialog=<html>List of space separated package names that will not be decompiled or indexed (saves RAM)<br>e.g. <code>android.support</code></html>
|
||||
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
|
||||
preferences.raw_cfg=Generate RAW CFG graphs
|
||||
preferences.font=Editor font
|
||||
|
||||
Reference in New Issue
Block a user