From 1ac2cdfc411de23d13e6b2ab3fce43b9dd9a6033 Mon Sep 17 00:00:00 2001 From: Jan Peter Stotz Date: Wed, 26 Dec 2018 17:08:51 +0100 Subject: [PATCH 1/9] fix: wait time for background jobs too short --- jadx-gui/src/main/java/jadx/gui/jobs/BackgroundJob.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundJob.java b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundJob.java index f457b87fc..cd86c3747 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundJob.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundJob.java @@ -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); } }); } From fd3498add6d830050bc8abb1d26325964e24d263 Mon Sep 17 00:00:00 2001 From: Donlon Date: Sun, 6 Jan 2019 19:02:37 +0800 Subject: [PATCH 2/9] fix: show method alias in "method not decompiled" messages (#410) --- .../src/main/java/jadx/core/codegen/MethodGen.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 3c189154f..2f343a652 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -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); From f60bb6b121d9009e025a6f5839cbb34e0825210c Mon Sep 17 00:00:00 2001 From: Jan S Date: Sun, 6 Jan 2019 13:46:54 +0100 Subject: [PATCH 3/9] fix: various UI improvements (#419) * fixed wait time for background jobs * enable multi-threaded decompiling * added preference for excluding certain packages from decompiling and indexing * show message dialog in case classes are not indexed because of low memory * added heap usage bar for visualizing Java memory usage --- .../src/main/java/jadx/gui/JadxWrapper.java | 29 ++++++++ .../java/jadx/gui/jobs/BackgroundWorker.java | 4 ++ .../main/java/jadx/gui/jobs/DecompileJob.java | 2 +- .../src/main/java/jadx/gui/jobs/IndexJob.java | 2 +- .../java/jadx/gui/settings/JadxSettings.java | 21 ++++++ .../jadx/gui/settings/JadxSettingsWindow.java | 24 +++++++ .../main/java/jadx/gui/ui/HeapUsageBar.java | 68 +++++++++++++++++++ .../src/main/java/jadx/gui/ui/MainWindow.java | 16 ++++- .../resources/i18n/Messages_en_US.properties | 9 +++ 9 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index 5e79fc301..f11a0bcf1 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -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 getClasses() { return decompiler.getClasses(); } + /** + * Get all classes that are not excluded by the excluded packages settings + * @return + */ + public List getIncludedClasses() { + List 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 getPackages() { return decompiler.getPackages(); } @@ -79,4 +104,8 @@ public class JadxWrapper { public File getOpenFile() { return openFile; } + + public JadxSettings getSettings() { + return settings; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java index 00e13f951..59ad04e39 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/BackgroundWorker.java @@ -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 { 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); diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java index df4af302f..983165067 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/DecompileJob.java @@ -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() { diff --git a/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java b/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java index 2d94456ab..ed1da813f 100644 --- a/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java +++ b/jadx-gui/src/main/java/jadx/gui/jobs/IndexJob.java @@ -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() { diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java index 56e1ba0d8..c88a6ba3e 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -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; } diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java index af2bd3f13..e61027f74 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -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())); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java b/jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java new file mode 100644 index 000000000..29abbdb45 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/HeapUsageBar.java @@ -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(); + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 5b8bf67c8..7315cc828 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -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); } diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index a0705904d..839443e44 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -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=Jadx is running low on memory. Therefore %d classes were not indexed.
If you want all classes to be indexed restart Jadx with increased maximum heap size. + +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=List of space separated package names that will not be decompiled or indexed (saves RAM)
e.g. android.support preferences.cfg=Generate methods CFG graphs (in 'dot' format) preferences.raw_cfg=Generate RAW CFG graphs preferences.font=Editor font From ddaf0375dc4e69ee0b66cf9572770bd6bcee1725 Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 7 Jan 2019 11:22:36 +0300 Subject: [PATCH 4/9] docs: add pyjadx link in readme (#424) --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 387710a3c..cadd85d3d 100644 --- a/README.md +++ b/README.md @@ -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 From 1272ae2d4dcc6c48fcabcdee493346192c8a9b8a Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 10 Jan 2019 23:45:52 +0300 Subject: [PATCH 5/9] fix(gui): don't skip indexing code lines starting with '}' (#426) --- .../gui/utils/search/TextSearchIndex.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/jadx-gui/src/main/java/jadx/gui/utils/search/TextSearchIndex.java b/jadx-gui/src/main/java/jadx/gui/utils/search/TextSearchIndex.java index 8e577f6da..92a7fbdfb 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/search/TextSearchIndex.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/search/TextSearchIndex.java @@ -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) { From a932c7c569de17550a9d00768f57939a31c7fe21 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 12 Jan 2019 13:06:41 +0300 Subject: [PATCH 6/9] build: add java 11 to build on travis --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4dbcf3303..6e13ba0f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 From 727285e3dfcd64a196f60e47c9308fd15ee281f5 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 12 Jan 2019 14:01:20 +0300 Subject: [PATCH 7/9] chore: update dependencies and gradle --- build.gradle | 9 +++----- gradle/wrapper/gradle-wrapper.properties | 2 +- jadx-core/build.gradle | 12 +++++----- .../jadx/tests/api/utils/CountString.java | 23 ++++++++++--------- jadx-gui/build.gradle | 8 +++---- 5 files changed, 26 insertions(+), 28 deletions(-) diff --git a/build.gradle b/build.gradle index 48299476c..d0aef43b7 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a95009c3b..558870dad 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/jadx-core/build.gradle b/jadx-core/build.gradle index fe8bc4327..edb6beb52 100644 --- a/jadx-core/build.gradle +++ b/jadx-core/build.gradle @@ -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' } diff --git a/jadx-core/src/test/java/jadx/tests/api/utils/CountString.java b/jadx-core/src/test/java/jadx/tests/api/utils/CountString.java index 489796137..58382a41e 100644 --- a/jadx-core/src/test/java/jadx/tests/api/utils/CountString.java +++ b/jadx-core/src/test/java/jadx/tests/api/utils/CountString.java @@ -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 { 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); } } diff --git a/jadx-gui/build.gradle b/jadx-gui/build.gradle index 956922536..65b3dd065 100644 --- a/jadx-gui/build.gradle +++ b/jadx-gui/build.gradle @@ -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 { From 72b2663949555b03aeb864775d84f163b91bcea7 Mon Sep 17 00:00:00 2001 From: Jan S Date: Sat, 12 Jan 2019 19:12:28 +0100 Subject: [PATCH 8/9] fix: ArrayIndexOutOfBoundsException in string concatenation visitor (#427) * fix: ArrayIndexOutOfBoundsException in string concatenation visitor * fix: typo in comment * fix: StringBuilder chain processing created wrong code * test: simple JUnit test cases added for testing StringBuilder chain processing (chains that can be and that can't be simplified) --- .../dex/instructions/CallMthInterface.java | 8 ++ .../core/dex/instructions/InvokeNode.java | 2 +- .../instructions/mods/ConstructorInsn.java | 3 +- .../core/dex/visitors/SimplifyVisitor.java | 27 ++++-- .../SimplifyVisitorStringBuilderTest.java | 94 +++++++++++++++++++ 5 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/SimplifyVisitorStringBuilderTest.java diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java b/jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java new file mode 100644 index 000000000..f5b8a84ee --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/CallMthInterface.java @@ -0,0 +1,8 @@ +package jadx.core.dex.instructions; + +import jadx.core.dex.info.MethodInfo; + +public interface CallMthInterface { + + public MethodInfo getCallMth(); +} diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java index 5baa632cd..217fa08e5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index b4451df7c..43d4a7625 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -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; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index 49ab39228..a7109faff 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java @@ -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 "text " + 1 + " text". + * + * @param mth + * @param insn + * @return + */ private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) { MethodInfo callMth = ((InvokeNode) insn).getCallMth(); @@ -201,7 +204,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; diff --git a/jadx-core/src/test/java/jadx/tests/integration/SimplifyVisitorStringBuilderTest.java b/jadx-core/src/test/java/jadx/tests/integration/SimplifyVisitorStringBuilderTest.java new file mode 100644 index 000000000..5baf911d4 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/SimplifyVisitorStringBuilderTest.java @@ -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();")); + } +} From 7c353a6c6f056a058a0fb0380f0ea00936b1153d Mon Sep 17 00:00:00 2001 From: Jan S Date: Tue, 15 Jan 2019 11:05:45 +0100 Subject: [PATCH 9/9] fix(gui): unsynchronized search index creation results in NullPointerException upon performing search (#429) --- jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java | 1 + jadx-gui/src/main/java/jadx/gui/utils/search/CodeIndex.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java index 5cc811299..62c9dad58 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/SearchDialog.java @@ -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); } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/search/CodeIndex.java b/jadx-gui/src/main/java/jadx/gui/utils/search/CodeIndex.java index 2c7328972..6aaf8e6e2 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/search/CodeIndex.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/search/CodeIndex.java @@ -23,7 +23,7 @@ public class CodeIndex implements SearchIndex { } @Override - public void put(StringRef str, T value) { + public synchronized void put(StringRef str, T value) { if (str == null || str.length() == 0) { return; }