From 43538902a3334c09fd5ddf42e0292335734c0b0b Mon Sep 17 00:00:00 2001 From: Jan S Date: Sat, 9 Mar 2019 15:20:34 +0100 Subject: [PATCH 01/15] fix: restore support for AAR files (issue #95) (PR #464) --- jadx-core/src/main/java/jadx/core/utils/files/InputFile.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java index 0d2cd18e8..470d26118 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java @@ -59,8 +59,8 @@ public class InputFile { loadFromZip(".dex"); return; } - if (fileName.endsWith(".jar")) { - // check if jar contains '.dex' files + if (fileName.endsWith(".jar") || fileName.endsWith(".aar")) { + // check if jar/aar contains '.dex' files if (loadFromZip(".dex")) { return; } From 7243ab5cb66b873068ebd8dcf618bb2ee9d6bc95 Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 12 Mar 2019 19:35:05 +0300 Subject: [PATCH 02/15] fix: don't replace resources names with field names (#465) --- .../jadx/core/xmlgen/BinaryXMLParser.java | 43 +++++-------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java b/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java index 63b730a96..d0dedc8ac 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java @@ -15,8 +15,6 @@ import org.slf4j.LoggerFactory; import jadx.api.ResourcesLoader; import jadx.core.codegen.CodeWriter; import jadx.core.dex.info.ConstStorage; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.StringUtils; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -40,7 +38,6 @@ public class BinaryXMLParser extends CommonBinaryParser { private static final boolean ATTR_NEW_LINE = false; private final Map styleMap = new HashMap<>(); - private final Map localStyleMap = new HashMap<>(); private final Map resNames; private final Map nsMap = new HashMap<>(); private Set nsMapGenerated; @@ -63,16 +60,7 @@ public class BinaryXMLParser extends CommonBinaryParser { this.rootNode = rootNode; try { readAndroidRStyleClass(); - // add application constants ConstStorage constStorage = rootNode.getConstValues(); - Map constFields = constStorage.getGlobalConstFields(); - for (Map.Entry entry : constFields.entrySet()) { - Object key = entry.getKey(); - FieldNode field = entry.getValue(); - if (field.getType().equals(ArgType.INT) && key instanceof Integer) { - localStyleMap.put((Integer) key, field); - } - } resNames = constStorage.getResourcesNames(); } catch (Exception e) { throw new JadxRuntimeException("BinaryXMLParser init error", e); @@ -381,38 +369,27 @@ public class BinaryXMLParser extends CommonBinaryParser { private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData, String shortNsName, String attrName) { - if (attrValDataType == TYPE_REFERENCE) { // reference custom processing String name = styleMap.get(attrValData); if (name != null) { writer.add("@style/").add(name.replaceAll("_", ".")); } else { - FieldNode field = localStyleMap.get(attrValData); - if (field != null) { - String cls = field.getParentClass().getShortName().toLowerCase(); + String resName = resNames.get(attrValData); + if (resName != null) { writer.add("@"); - if ("id".equals(cls)) { - writer.add('+'); + if (resName.startsWith("id/")) { + writer.add("+"); } - writer.add(cls).add("/").add(field.getName()); + writer.add(resName); } else { - String resName = resNames.get(attrValData); + resName = ValuesParser.getAndroidResMap().get(attrValData); if (resName != null) { - writer.add("@"); - if (resName.startsWith("id/")) { - writer.add("+"); - } - writer.add(resName); + writer.add("@android:").add(resName); + } else if (attrValData == 0) { + writer.add("@null"); } else { - resName = ValuesParser.getAndroidResMap().get(attrValData); - if (resName != null) { - writer.add("@android:").add(resName); - } else if (attrValData == 0) { - writer.add("@null"); - } else { - writer.add("0x").add(Integer.toHexString(attrValData)); - } + writer.add("0x").add(Integer.toHexString(attrValData)); } } } From 6116a750226c18176840702026973dc804d907fa Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 12 Mar 2019 20:27:25 +0300 Subject: [PATCH 03/15] fix: rename R fields using resource names (#465) --- .../utils/android/AndroidResourcesUtils.java | 60 +++++++++++++++---- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java index 53d49dfca..3faf6eaf1 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java @@ -1,18 +1,23 @@ package jadx.core.utils.android; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import com.android.dx.rop.code.AccessFlags; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.core.codegen.ClassGen; import jadx.core.codegen.CodeWriter; +import jadx.core.deobf.NameMapper; import jadx.core.dex.attributes.AType; +import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.ConstStorage; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; @@ -86,6 +91,7 @@ public class AndroidResourcesUtils { } private static void addResourceFields(ClassNode resCls, ResourceStorage resStorage, boolean rClsExists) { + Map resFieldsMap = fillResFieldsMap(resCls); Map innerClsMap = new TreeMap<>(); if (rClsExists) { for (ClassNode innerClass : resCls.getInnerClasses()) { @@ -93,18 +99,14 @@ public class AndroidResourcesUtils { } } for (ResourceEntry resource : resStorage.getResources()) { - ClassNode typeCls = innerClsMap.computeIfAbsent(resource.getTypeName(), name -> { - ClassNode newTypeCls = new ClassNode(resCls.dex(), resCls.getFullName() + "$" + name, - AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL); - resCls.addInnerClass(newTypeCls); - if (rClsExists) { - newTypeCls.addAttr(AType.COMMENTS, "added by JADX"); - } - return newTypeCls; - }); - FieldNode rField = typeCls.searchFieldByName(resource.getKeyName()); + ClassNode typeCls = innerClsMap.computeIfAbsent( + resource.getTypeName(), + name -> addClassForResType(resCls, rClsExists, name) + ); + String resName = resource.getKeyName(); + FieldNode rField = typeCls.searchFieldByName(resName); if (rField == null) { - FieldInfo rFieldInfo = FieldInfo.from(typeCls.dex(), typeCls.getClassInfo(), resource.getKeyName(), ArgType.INT); + FieldInfo rFieldInfo = FieldInfo.from(typeCls.dex(), typeCls.getClassInfo(), resName, ArgType.INT); rField = new FieldNode(typeCls, rFieldInfo, AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL); rField.addAttr(FieldInitAttr.constValue(resource.getId())); typeCls.getFields().add(rField); @@ -112,6 +114,42 @@ public class AndroidResourcesUtils { rField.addAttr(AType.COMMENTS, "added by JADX"); } } + FieldNode fieldNode = resFieldsMap.get(resource.getId()); + if (fieldNode != null + && !fieldNode.getName().equals(resName) + && NameMapper.isValidIdentifier(resName)) { + fieldNode.getFieldInfo().setAlias(resName); + } } } + + @NotNull + private static ClassNode addClassForResType(ClassNode resCls, boolean rClsExists, String typeName) { + ClassNode newTypeCls = new ClassNode(resCls.dex(), resCls.getFullName() + "$" + typeName, + AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL); + resCls.addInnerClass(newTypeCls); + if (rClsExists) { + newTypeCls.addAttr(AType.COMMENTS, "added by JADX"); + } + return newTypeCls; + } + + @NotNull + private static Map fillResFieldsMap(ClassNode resCls) { + Map resFieldsMap = new HashMap<>(); + ConstStorage constStorage = resCls.root().getConstValues(); + Map constFields = constStorage.getGlobalConstFields(); + for (Map.Entry entry : constFields.entrySet()) { + Object key = entry.getKey(); + FieldNode field = entry.getValue(); + AccessInfo accessFlags = field.getAccessFlags(); + if (field.getType().equals(ArgType.INT) + && accessFlags.isStatic() + && accessFlags.isFinal() + && key instanceof Integer) { + resFieldsMap.put((Integer) key, field); + } + } + return resFieldsMap; + } } From a4a8b05ef089d605d6a535e4218cf02900dcebc2 Mon Sep 17 00:00:00 2001 From: tRuNKator <34552981+tRuNKator@users.noreply.github.com> Date: Thu, 14 Mar 2019 17:46:37 +0500 Subject: [PATCH 04/15] fix: replace dot character with underscore in style resource name (PR #466) --- .../jadx/core/utils/android/AndroidResourcesUtils.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java index 3faf6eaf1..3efb5125a 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidResourcesUtils.java @@ -99,11 +99,17 @@ public class AndroidResourcesUtils { } } for (ResourceEntry resource : resStorage.getResources()) { + final String resTypeName = resource.getTypeName(); ClassNode typeCls = innerClsMap.computeIfAbsent( - resource.getTypeName(), + resTypeName, name -> addClassForResType(resCls, rClsExists, name) ); - String resName = resource.getKeyName(); + final String resName; + if ("style".equals(resTypeName)) { + resName = resource.getKeyName().replace('.', '_'); + } else { + resName = resource.getKeyName(); + } FieldNode rField = typeCls.searchFieldByName(resName); if (rField == null) { FieldInfo rFieldInfo = FieldInfo.from(typeCls.dex(), typeCls.getClassInfo(), resName, ArgType.INT); From bce86d32110f7ee39b0591a58c340eb0c369bd88 Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 14 Mar 2019 18:10:26 +0300 Subject: [PATCH 05/15] build: check env variables for deploy stages (i.e skip deploy in forks) --- .travis.yml | 48 ++++++++++++++------------------------- scripts/bintray-upload.sh | 6 ++--- scripts/travis-master.sh | 2 +- scripts/travis-release.sh | 2 +- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e13ba0f8..4b1ee63d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,7 @@ before_install: - chmod +x gradlew # override install to skip 'gradle assemble' -install: - - true +install: true env: global: @@ -21,34 +20,21 @@ env: - JADX_LAST_TAG=$(git describe --abbrev=0 --tags) - JADX_VERSION="${JADX_LAST_TAG:1}-b$TRAVIS_BUILD_NUMBER-$(git rev-parse --short HEAD)" -matrix: +jdk: + - openjdk8 + - oraclejdk8 + - openjdk11 + +script: ./gradlew clean build + +jobs: include: - - env: JDK=oracle-8 - jdk: oraclejdk8 - - env: JDK=openjdk11 - jdk: openjdk11 + - stage: deploy-unstable + jdk: openjdk8 + if: branch = master AND env(BINTRAY_USER) IS present + script: bash scripts/travis-master.sh -script: - - java -version - - ./gradlew clean build - -deploy: - - provider: script - skip_cleanup: true - on: - branch: master - tags: false - condition: $JDK = oracle-8 - script: bash scripts/travis-master.sh - - - provider: script - skip_cleanup: true - on: - branch: release - tags: false - condition: $JDK = oracle-8 - script: bash scripts/travis-release.sh - -notifications: - email: - - skylot@gmail.com + - stage: deploy-release + jdk: openjdk8 + if: branch = release AND env(GH_TOKEN) IS present + script: bash scripts/travis-release.sh diff --git a/scripts/bintray-upload.sh b/scripts/bintray-upload.sh index 3a6a35783..a8982f981 100644 --- a/scripts/bintray-upload.sh +++ b/scripts/bintray-upload.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash -set -xe +set -e export JFROG_CLI_OFFER_CONFIG=false export JFROG_CLI_LOG_LEVEL=DEBUG npm install -g jfrog-cli-go -TARGET=skylot/jadx/${BINTRAY_PACKAGE}/v${JADX_VERSION} -CREDENTIALS="--user=skylot --key=${BINTRAY_KEY}" +TARGET=${BINTRAY_USER}/jadx/${BINTRAY_PACKAGE}/v${JADX_VERSION} +CREDENTIALS="--user=${BINTRAY_USER} --key=${BINTRAY_KEY}" jfrog bt version-create ${TARGET} ${CREDENTIALS} --desc=${JADX_VERSION} jfrog bt upload 'build/jadx.*\.(zip|exe)' ${TARGET} ${CREDENTIALS} --regexp=true --publish=true diff --git a/scripts/travis-master.sh b/scripts/travis-master.sh index 75c7b0da0..07c77ec7f 100644 --- a/scripts/travis-master.sh +++ b/scripts/travis-master.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -xe +set -e # upload coverage to codecov ./gradlew clean build jacocoTestReport diff --git a/scripts/travis-release.sh b/scripts/travis-release.sh index bdad6ef2f..49c7ba64f 100644 --- a/scripts/travis-release.sh +++ b/scripts/travis-release.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -xe +set -e npm install -g semantic-release npm install -g semantic-release/exec From 51a9c741a50af31f30305f843961a9c8e8ed6bc9 Mon Sep 17 00:00:00 2001 From: Skylot Date: Fri, 15 Mar 2019 19:37:19 +0300 Subject: [PATCH 06/15] chore: update dependencies and gradle --- build.gradle | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- jadx-core/build.gradle | 14 +++++++++----- jadx-gui/build.gradle | 13 +++++++------ 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index fee10c7d3..e27970da9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'org.sonarqube' version '2.7' - id 'com.github.ben-manes.versions' version '0.20.0' + id 'com.github.ben-manes.versions' version '0.21.0' } ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev" @@ -35,12 +35,12 @@ allprojects { } dependencies { - compile 'org.slf4j:slf4j-api:1.7.25' + compile 'org.slf4j:slf4j-api:1.7.26' testCompile 'ch.qos.logback:logback-classic:1.2.3' testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:2.1' - testCompile 'org.mockito:mockito-core:2.23.4' + testCompile 'org.mockito:mockito-core:2.25.0' testCompile 'org.spockframework:spock-core:1.1-groovy-2.4' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 558870dad..44e7c4d1d 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-5.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/jadx-core/build.gradle b/jadx-core/build.gradle index edb6beb52..037d9b9cb 100644 --- a/jadx-core/build.gradle +++ b/jadx-core/build.gradle @@ -5,13 +5,17 @@ dependencies { compile files('lib/dx-1.16.jar') compile 'commons-io:commons-io:2.6' - compile 'org.ow2.asm:asm:7.0' - compile 'org.jetbrains:annotations:16.0.3' - compile 'uk.com.robust-it:cloning:1.9.11' + compile 'org.ow2.asm:asm:7.1' + compile 'org.jetbrains:annotations:17.0.0' + compile 'uk.com.robust-it:cloning:1.9.12' - testCompile 'org.smali:smali:2.2.5' - testCompile 'org.smali:baksmali:2.2.5' + testCompile 'org.smali:smali:2.2.6' + testCompile 'org.smali:baksmali:2.2.6' testCompile 'org.apache.commons:commons-lang3:3.8.1' + + // update dependency in smali + testCompile 'com.google.guava:guava:27.1-jre' + testCompile 'com.beust:jcommander:1.74' } diff --git a/jadx-gui/build.gradle b/jadx-gui/build.gradle index d4becc1fc..6ba64d8fe 100644 --- a/jadx-gui/build.gradle +++ b/jadx-gui/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'edu.sc.seis.launch4j' version '2.4.4' - id 'com.github.johnrengelman.shadow' version '4.0.3' + id 'edu.sc.seis.launch4j' version '2.4.5' + id 'com.github.johnrengelman.shadow' version '5.0.0' } apply plugin: 'application' @@ -13,7 +13,8 @@ targetCompatibility = JavaVersion.VERSION_1_8 dependencies { compile(project(":jadx-core")) compile(project(":jadx-cli")) - compile 'com.fifesoft:rsyntaxtextarea:3.0.0' + + compile 'com.fifesoft:rsyntaxtextarea:3.0.2' 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' @@ -21,9 +22,9 @@ dependencies { compile 'org.apache.commons:commons-lang3:3.8.1' compile 'org.apache.commons:commons-text:1.6' - compile 'io.reactivex.rxjava2:rxjava:2.2.5' - compile "com.github.akarnokd:rxjava2-swing:0.3.3" - compile 'com.android.tools.build:apksig:3.3.0' + compile 'io.reactivex.rxjava2:rxjava:2.2.7' + compile "com.github.akarnokd:rxjava2-swing:0.3.4" + compile 'com.android.tools.build:apksig:3.3.2' } applicationDistribution.with { From 7654661b776c76abcb97915b46b9d1ace41c7fc9 Mon Sep 17 00:00:00 2001 From: Skylot Date: Fri, 15 Mar 2019 22:12:03 +0300 Subject: [PATCH 07/15] fix: inline desugared lambda classes (#467) --- .../main/java/jadx/api/JadxDecompiler.java | 14 +++--- .../src/main/java/jadx/core/ProcessClass.java | 12 ++--- .../main/java/jadx/core/codegen/ClassGen.java | 2 +- .../main/java/jadx/core/codegen/CodeGen.java | 17 ++++--- .../java/jadx/core/codegen/CodeWriter.java | 7 ++- .../main/java/jadx/core/codegen/InsnGen.java | 13 ++--- .../java/jadx/core/dex/nodes/ClassNode.java | 26 +++++++++- .../jadx/core/dex/visitors/ClassModifier.java | 49 +++++++++++-------- .../dex/visitors/DependencyCollector.java | 4 ++ .../java/jadx/core/utils/RegionUtils.java | 7 ++- .../java/jadx/tests/api/IntegrationTest.java | 4 +- .../jadx/tests/external/BaseExternalTest.java | 12 ++--- 12 files changed, 100 insertions(+), 67 deletions(-) diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index db84e02e7..ccd40ab34 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory; import jadx.core.Jadx; import jadx.core.ProcessClass; -import jadx.core.codegen.CodeGen; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; @@ -59,7 +58,6 @@ public final class JadxDecompiler { private RootNode root; private List passes; - private CodeGen codeGen; private List classes; private List resources; @@ -97,7 +95,6 @@ public final class JadxDecompiler { void init() { this.passes = Jadx.getPassesList(args); - this.codeGen = new CodeGen(); } void reset() { @@ -106,7 +103,6 @@ public final class JadxDecompiler { xmlParser = null; root = null; passes = null; - codeGen = null; } public static String getVersion() { @@ -215,9 +211,11 @@ public final class JadxDecompiler { List clsList = new ArrayList<>(classNodeList.size()); classesMap.clear(); for (ClassNode classNode : classNodeList) { - JavaClass javaClass = new JavaClass(classNode, this); - clsList.add(javaClass); - classesMap.put(classNode, javaClass); + if (!classNode.contains(AFlag.DONT_GENERATE)) { + JavaClass javaClass = new JavaClass(classNode, this); + clsList.add(javaClass); + classesMap.put(classNode, javaClass); + } } classes = Collections.unmodifiableList(clsList); } @@ -289,7 +287,7 @@ public final class JadxDecompiler { } void processClass(ClassNode cls) { - ProcessClass.process(cls, passes, codeGen); + ProcessClass.process(cls, passes, true); } RootNode getRoot() { diff --git a/jadx-core/src/main/java/jadx/core/ProcessClass.java b/jadx-core/src/main/java/jadx/core/ProcessClass.java index 985510e9b..d35067806 100644 --- a/jadx-core/src/main/java/jadx/core/ProcessClass.java +++ b/jadx-core/src/main/java/jadx/core/ProcessClass.java @@ -2,8 +2,6 @@ package jadx.core; import java.util.List; -import org.jetbrains.annotations.Nullable; - import jadx.core.codegen.CodeGen; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.visitors.DepthTraversal; @@ -19,8 +17,8 @@ public final class ProcessClass { private ProcessClass() { } - public static void process(ClassNode cls, List passes, @Nullable CodeGen codeGen) { - if (codeGen == null && cls.getState() == PROCESSED) { + public static void process(ClassNode cls, List passes, boolean generateCode) { + if (!generateCode && cls.getState() == PROCESSED) { return; } synchronized (getSyncObj(cls)) { @@ -33,9 +31,9 @@ public final class ProcessClass { } cls.setState(PROCESSED); } - if (cls.getState() == PROCESSED && codeGen != null) { + if (cls.getState() == PROCESSED && generateCode) { processDependencies(cls, passes); - codeGen.visit(cls); + CodeGen.generate(cls); } } catch (Exception e) { ErrorsCounter.classError(cls, e.getClass().getSimpleName(), e); @@ -48,6 +46,6 @@ public final class ProcessClass { } private static void processDependencies(ClassNode cls, List passes) { - cls.getDependencies().forEach(depCls -> process(depCls, passes, null)); + cls.getDependencies().forEach(depCls -> process(depCls, passes, false)); } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index dec89c983..b06c5aa4b 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -98,7 +98,7 @@ public class ClassGen { imports.clear(); } clsCode.add(clsBody); - return clsCode; + return clsCode.finish(); } public void addClassCode(CodeWriter code) throws CodegenException { diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java b/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java index eae73cb09..926443efc 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeGen.java @@ -1,15 +1,20 @@ package jadx.core.codegen; +import jadx.core.dex.attributes.AFlag; import jadx.core.dex.nodes.ClassNode; import jadx.core.utils.exceptions.CodegenException; public class CodeGen { - public boolean visit(ClassNode cls) throws CodegenException { - ClassGen clsGen = new ClassGen(cls, cls.root().getArgs()); - CodeWriter clsCode = clsGen.makeClass(); - clsCode.finish(); - cls.setCode(clsCode); - return false; + public static void generate(ClassNode cls) throws CodegenException { + if (cls.contains(AFlag.DONT_GENERATE)) { + cls.setCode(CodeWriter.EMPTY); + } else { + ClassGen clsGen = new ClassGen(cls, cls.root().getArgs()); + cls.setCode(clsGen.makeClass()); + } + } + + private CodeGen() { } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index 31a2e2783..4f5adc847 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -24,6 +24,8 @@ public class CodeWriter { public static final String NL = System.getProperty("line.separator"); public static final String INDENT_STR = " "; + public static final CodeWriter EMPTY = new CodeWriter().finish(); + private static final boolean ADD_LINE_NUMBERS = false; private static final String[] INDENT_CACHE = { @@ -250,7 +252,7 @@ public class CodeWriter { return lineMap; } - public void finish() { + public CodeWriter finish() { removeFirstEmptyLine(); buf.trimToSize(); code = buf.toString(); @@ -266,11 +268,12 @@ public class CodeWriter { it.remove(); } } + return this; } private void removeFirstEmptyLine() { int len = NL.length(); - if (buf.substring(0, len).equals(NL)) { + if (buf.length() > len && buf.substring(0, len).equals(NL)) { buf.delete(0, len); } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index e148f1c7f..decaabd21 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -48,7 +48,6 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.utils.ErrorsCounter; import jadx.core.utils.RegionUtils; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -559,7 +558,7 @@ public class InsnGen { throws CodegenException { ClassNode cls = mth.dex().resolveClass(insn.getClassType()); if (cls != null && cls.contains(AFlag.ANONYMOUS_CLASS) && !fallback) { - inlineAnonymousConstr(code, cls, insn); + inlineAnonymousConstructor(code, cls, insn); return; } if (insn.isSelf()) { @@ -577,20 +576,14 @@ public class InsnGen { generateMethodArguments(code, insn, 0, callMth); } - private void inlineAnonymousConstr(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException { - // anonymous class construction - if (cls.contains(AFlag.DONT_GENERATE)) { - code.add("/* anonymous class already generated */"); - ErrorsCounter.methodWarn(mth, "Anonymous class already generated: " + cls); - return; - } + private void inlineAnonymousConstructor(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException { + cls.add(AFlag.DONT_GENERATE); ArgType parent; if (cls.getInterfaces().size() == 1) { parent = cls.getInterfaces().get(0); } else { parent = cls.getSuperClass(); } - cls.add(AFlag.DONT_GENERATE); MethodNode defCtr = cls.getDefaultConstructor(); if (defCtr != null) { if (RegionUtils.notEmpty(defCtr.getRegion())) { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 73b028650..3ae9e8de3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory; import jadx.core.Consts; import jadx.core.codegen.CodeWriter; +import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.attributes.nodes.SourceFileAttr; @@ -123,7 +124,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { accFlagsValue = cls.getAccessFlags(); } this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS); - + markAnonymousClass(this); buildCache(); } catch (Exception e) { throw new JadxRuntimeException("Error decode class: " + clsInfo, e); @@ -394,6 +395,29 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { && getDefaultConstructor() != null; } + public boolean isLambdaCls() { + return accessFlags.isSynthetic() && accessFlags.isFinal() + && clsInfo.getType().getObject().contains(".-$$Lambda$") + && countStaticFields() == 0; + } + + private int countStaticFields() { + int c = 0; + for (FieldNode field : fields) { + if (field.getAccessFlags().isStatic()) { + c++; + } + } + return c; + } + + private static void markAnonymousClass(ClassNode cls) { + if (cls.isAnonymous() || cls.isLambdaCls()) { + cls.add(AFlag.ANONYMOUS_CLASS); + cls.add(AFlag.DONT_GENERATE); + } + } + @Nullable public MethodNode getClassInitMth() { return searchMethodByName("()V"); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index 04a493bcd..963130f4d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -54,40 +54,46 @@ public class ClassModifier extends AbstractVisitor { cls.add(AFlag.DONT_GENERATE); return false; } - removeSyntheticFields(cls); - cls.getMethods().forEach(mth -> removeSyntheticMethods(cls, mth)); - cls.getMethods().forEach(ClassModifier::removeEmptyMethods); - markAnonymousClass(cls); + removeSyntheticFields(cls); + cls.getMethods().forEach(ClassModifier::removeSyntheticMethods); + cls.getMethods().forEach(ClassModifier::removeEmptyMethods); return false; } private void markAnonymousClass(ClassNode cls) { if (cls.isAnonymous()) { cls.add(AFlag.ANONYMOUS_CLASS); + cls.add(AFlag.DONT_GENERATE); } } + /** + * Remove synthetic fields if type is outer class or class will be inlined (anonymous) + */ private static void removeSyntheticFields(ClassNode cls) { - if (!cls.getClassInfo().isInner() || cls.getAccessFlags().isStatic()) { + if (cls.getAccessFlags().isStatic()) { return; } - // remove fields if it is synthetic and type is a outer class - for (FieldNode field : cls.getFields()) { - if (field.getAccessFlags().isSynthetic() && field.getType().isObject()) { - ClassInfo clsInfo = ClassInfo.fromType(cls.root(), field.getType()); - ClassNode fieldsCls = cls.dex().resolveClass(clsInfo); - ClassInfo parentClass = cls.getClassInfo().getParentClass(); - if (fieldsCls != null && parentClass.equals(fieldsCls.getClassInfo())) { - int found = 0; - for (MethodNode mth : cls.getMethods()) { - if (removeFieldUsageFromConstructor(mth, field, fieldsCls)) { - found++; + boolean inline = cls.contains(AFlag.ANONYMOUS_CLASS); + if (inline || cls.getClassInfo().isInner()) { + for (FieldNode field : cls.getFields()) { + if (field.getAccessFlags().isSynthetic() && field.getType().isObject()) { + ClassInfo clsInfo = ClassInfo.fromType(cls.root(), field.getType()); + ClassNode fieldsCls = cls.dex().resolveClass(clsInfo); + ClassInfo parentClass = cls.getClassInfo().getParentClass(); + if (fieldsCls != null + && (inline || parentClass.equals(fieldsCls.getClassInfo()))) { + int found = 0; + for (MethodNode mth : cls.getMethods()) { + if (removeFieldUsageFromConstructor(mth, field, fieldsCls)) { + found++; + } + } + if (found != 0) { + field.addAttr(new FieldReplaceAttr(fieldsCls.getClassInfo())); + field.add(AFlag.DONT_GENERATE); } - } - if (found != 0) { - field.addAttr(new FieldReplaceAttr(parentClass)); - field.add(AFlag.DONT_GENERATE); } } } @@ -133,7 +139,7 @@ public class ClassModifier extends AbstractVisitor { return true; } - private static void removeSyntheticMethods(ClassNode cls, MethodNode mth) { + private static void removeSyntheticMethods(MethodNode mth) { if (mth.isNoCode()) { return; } @@ -141,6 +147,7 @@ public class ClassModifier extends AbstractVisitor { if (!af.isSynthetic()) { return; } + ClassNode cls = mth.getParentClass(); if (removeBridgeMethod(cls, mth)) { if (Consts.DEBUG) { mth.addAttr(AType.COMMENTS, "Removed as synthetic bridge method"); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java index 558ef3fb4..92cffd845 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java @@ -11,6 +11,7 @@ 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.RegisterArg; +import jadx.core.dex.instructions.mods.ConstructorInsn; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.DexNode; @@ -90,6 +91,9 @@ public class DependencyCollector extends AbstractVisitor { } else if (insn instanceof InvokeNode) { ClassInfo declClass = ((InvokeNode) insn).getCallMth().getDeclClass(); addDep(dex, depList, declClass); + } else if (insn instanceof ConstructorInsn) { + ClassInfo declClass = ((ConstructorInsn) insn).getCallMth().getDeclClass(); + addDep(dex, depList, declClass); } } diff --git a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java index f4a9b5d0a..5ded8fe65 100644 --- a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java @@ -5,6 +5,8 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import org.jetbrains.annotations.Nullable; + import jadx.core.dex.attributes.AType; import jadx.core.dex.instructions.InsnType; import jadx.core.dex.nodes.BlockNode; @@ -136,7 +138,10 @@ public class RegionUtils { return !notEmpty(container); } - public static boolean notEmpty(IContainer container) { + public static boolean notEmpty(@Nullable IContainer container) { + if (container == null) { + return false; + } if (container instanceof IBlock) { return !((IBlock) container).getInstructions().isEmpty(); } else if (container instanceof IRegion) { diff --git a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java index 32fa837d3..de77a9773 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -139,7 +139,7 @@ public abstract class IntegrationTest extends TestUtils { protected void decompile(JadxDecompiler jadx, ClassNode cls) { List passes = getPassesList(jadx); - ProcessClass.process(cls, passes, new CodeGen()); + ProcessClass.process(cls, passes, true); } protected void decompileWithoutUnload(JadxDecompiler jadx, ClassNode cls) { @@ -168,7 +168,7 @@ public abstract class IntegrationTest extends TestUtils { protected void generateClsCode(ClassNode cls) { try { - new CodeGen().visit(cls); + CodeGen.generate(cls); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); diff --git a/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java b/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java index 58b29cc5d..17dd03908 100644 --- a/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java +++ b/jadx-core/src/test/java/jadx/tests/external/BaseExternalTest.java @@ -15,12 +15,11 @@ import jadx.api.JadxDecompiler; import jadx.api.JadxInternalAccess; import jadx.api.JavaClass; import jadx.core.Jadx; -import jadx.core.codegen.CodeGen; +import jadx.core.ProcessClass; import jadx.core.codegen.CodeWriter; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.tests.api.IntegrationTest; @@ -77,7 +76,7 @@ public abstract class BaseExternalTest extends IntegrationTest { for (ClassNode classNode : root.getClasses(true)) { String clsFullName = classNode.getClassInfo().getFullName(); if (clsPattern.matcher(clsFullName).matches()) { - if (processCls(mthPattern, passes, classNode)) { + if (processCls(jadx, mthPattern, passes, classNode)) { processed++; } } @@ -85,7 +84,7 @@ public abstract class BaseExternalTest extends IntegrationTest { assertThat("No classes processed", processed, greaterThan(0)); } - private boolean processCls(@Nullable Pattern mthPattern, List passes, ClassNode classNode) { + private boolean processCls(JadxDecompiler jadx, @Nullable Pattern mthPattern, List passes, ClassNode classNode) { classNode.load(); boolean decompile = false; if (mthPattern == null) { @@ -101,11 +100,8 @@ public abstract class BaseExternalTest extends IntegrationTest { if (!decompile) { return false; } - for (IDexTreeVisitor visitor : passes) { - DepthTraversal.visit(visitor, classNode); - } try { - new CodeGen().visit(classNode); + ProcessClass.process(classNode, passes, true); } catch (Exception e) { throw new JadxRuntimeException("Codegen failed", e); } From 214866fcb962c4ed672a55079ad725f9daf85758 Mon Sep 17 00:00:00 2001 From: Jan Peter Stotz Date: Sat, 16 Mar 2019 18:12:39 +0100 Subject: [PATCH 08/15] chore: About box and logos --- .../main/java/jadx/gui/ui/AboutDialog.java | 28 ++++++++++++++++-- .../src/main/java/jadx/gui/ui/MainWindow.java | 14 ++------- .../src/main/java/jadx/gui/utils/Utils.java | 11 +++++++ .../src/main/resources/icons-16/jadx-logo.png | Bin 0 -> 814 bytes .../src/main/resources/logos/jadx-logo.ico | Bin 9662 -> 15086 bytes .../src/main/resources/logos/jadx-logo.svg | 1 + 6 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 jadx-gui/src/main/resources/icons-16/jadx-logo.png create mode 100644 jadx-gui/src/main/resources/logos/jadx-logo.svg diff --git a/jadx-gui/src/main/java/jadx/gui/ui/AboutDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/AboutDialog.java index 9be61b539..4659aab39 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/AboutDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/AboutDialog.java @@ -2,9 +2,11 @@ package jadx.gui.ui; import javax.swing.*; import java.awt.*; +import java.net.URL; import jadx.api.JadxDecompiler; import jadx.gui.utils.NLS; +import jadx.gui.utils.Utils; class AboutDialog extends JDialog { private static final long serialVersionUID = 5763493590584039096L; @@ -16,7 +18,10 @@ class AboutDialog extends JDialog { public final void initUI() { Font font = new Font("Serif", Font.BOLD, 13); - JLabel name = new JLabel("jadx"); + URL logoURL = getClass().getResource("/logos/jadx-logo-48px.png"); + Icon logo = new ImageIcon(logoURL, "jadx logo"); + + JLabel name = new JLabel("jadx", logo, SwingConstants.CENTER); name.setFont(font); name.setAlignmentX(0.5f); @@ -24,10 +29,24 @@ class AboutDialog extends JDialog { desc.setFont(font); desc.setAlignmentX(0.5f); - JLabel version = new JLabel("version: " + JadxDecompiler.getVersion()); + JLabel version = new JLabel("jadx version: " + JadxDecompiler.getVersion()); version.setFont(font); version.setAlignmentX(0.5f); + String javaVm = System.getProperty("java.vm.name"); + String javaVer = System.getProperty("java.vm.version"); + + javaVm = javaVm == null ? "" : javaVm; + + JLabel javaVmLabel = new JLabel("Java VM: " + javaVm); + javaVmLabel.setFont(font); + javaVmLabel.setAlignmentX(0.5f); + + javaVer = javaVer == null ? "" : javaVer; + JLabel javaVerLabel = new JLabel("Java version: " + javaVer); + javaVerLabel.setFont(font); + javaVerLabel.setAlignmentX(0.5f); + JPanel textPane = new JPanel(); textPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); textPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS)); @@ -38,6 +57,9 @@ class AboutDialog extends JDialog { textPane.add(Box.createRigidArea(new Dimension(0, 10))); textPane.add(version); textPane.add(Box.createRigidArea(new Dimension(0, 20))); + textPane.add(javaVmLabel); + textPane.add(javaVerLabel); + textPane.add(Box.createRigidArea(new Dimension(0, 20))); JButton close = new JButton(NLS.str("tabs.close")); close.addActionListener(event -> dispose()); @@ -47,6 +69,8 @@ class AboutDialog extends JDialog { contentPane.add(textPane, BorderLayout.CENTER); contentPane.add(close, BorderLayout.PAGE_END); + Utils.setWindowIcons(this); + setModalityType(ModalityType.APPLICATION_MODAL); setTitle(NLS.str("about_dialog.title")); 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 4885eb93e..4783e984f 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -82,6 +82,7 @@ public class MainWindow extends JFrame { private static final ImageIcon ICON_PREF = Utils.openIcon("wrench"); private static final ImageIcon ICON_DEOBF = Utils.openIcon("lock_edit"); private static final ImageIcon ICON_LOG = Utils.openIcon("report"); + private static final ImageIcon ICON_JADX = Utils.openIcon("jadx-logo"); private final transient JadxWrapper wrapper; private final transient JadxSettings settings; @@ -116,16 +117,7 @@ public class MainWindow extends JFrame { registerBundledFonts(); initUI(); initMenuAndToolbar(); - setWindowIcons(); - } - - private void setWindowIcons() { - List icons = new ArrayList<>(); - icons.add(Utils.openImage("/logos/jadx-logo-16px.png")); - icons.add(Utils.openImage("/logos/jadx-logo-32px.png")); - icons.add(Utils.openImage("/logos/jadx-logo-48px.png")); - icons.add(Utils.openImage("/logos/jadx-logo.png")); - setIconImages(icons); + Utils.setWindowIcons(this); loadSettings(); checkForUpdate(); } @@ -462,7 +454,7 @@ public class MainWindow extends JFrame { logAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_L, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); - Action aboutAction = new AbstractAction(NLS.str("menu.about")) { + Action aboutAction = new AbstractAction(NLS.str("menu.about"), ICON_JADX) { @Override public void actionPerformed(ActionEvent e) { new AboutDialog().setVisible(true); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/Utils.java b/jadx-gui/src/main/java/jadx/gui/utils/Utils.java index f6f724273..8ca0bb926 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/Utils.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/Utils.java @@ -7,6 +7,8 @@ import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.io.InputStream; import java.net.URL; +import java.util.ArrayList; +import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -191,4 +193,13 @@ public class Utils { } return sb.toString().trim(); } + + public static void setWindowIcons(Window window) { + List icons = new ArrayList<>(); + icons.add(Utils.openImage("/logos/jadx-logo-16px.png")); + icons.add(Utils.openImage("/logos/jadx-logo-32px.png")); + icons.add(Utils.openImage("/logos/jadx-logo-48px.png")); + icons.add(Utils.openImage("/logos/jadx-logo.png")); + window.setIconImages(icons); + } } diff --git a/jadx-gui/src/main/resources/icons-16/jadx-logo.png b/jadx-gui/src/main/resources/icons-16/jadx-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a2d08566be02ca93b41eb6d9a24255ed28e18e GIT binary patch literal 814 zcmV+}1JV46P)LQ+-I&K^T6VTj^}m+0vGdtvStENM&WgqEbS%tYKd2tslqx!1{ zj^Dj^@AKSy_j}#|LI@@!PrGgJdX1*c;c!F=g+jLD^Z6i^O5e4ZOeZcjT&|g%?-w|j zTbi;|lWMZLor1H5T1J`-Dwda@4~b;vhpLLo#c%93JKLrL6$QH%58GXY!XPUkdf7&D z_eu+kmn}&qF*3P-tWD1#-Cf<|F*3QFaye8)ox1Y2^WJm+dD74bq-_RbULoS`e-+eIrPpR*I?L3_mE#8u_T>+$ISgm&@4dgcr;1^lELSNfCL6I zk%58tV#?*v2Wv81^^+&KpP<^R#)R7eZvTA*$HrlP@>lf6d+~f*2CR8muxT~WJI%<< z&f3GJudUs=1GDDEwp1y>6$k)<08=vyndmJAAE)^dzYdwg!}}>b*u5SJcX@0y z=BiZSMMmRu<1z43a`4Ue5@Y*vSaNq#5zH}ah`jxHyQ>myRtu^hw&QVODLP;GA>eKE zB_cK+#oy36B!Q~Zhy}~Tv8e7Ok|a?$w`K#ZUJuK@{h$M!R=XEB+~5A7cYF!Qh2C-; zD*5qu(xQ~8ebR}Z0Vfoqa1`Hc!tmHAIMiXsTtUByv<>sI!q5&5g8ggLoiiW`3Wh>7 zoyBUY91&vi2#4B}I!tHzf*RX6dWH!ciRTa^;Ih(pSF0Go+DvdhZ3COrN^~n$<4)Hj z6LaFV#>PSFFr`dMQl<5z?y!Zqkiy*SBZrEgk%~=ESX|V4oy@O2PVAl!6O_w(K`Eat zZ!{V$XZBmU&KEyQ{1^DEo??4;?D*c@WpT70{LK_6Pf#4u*Ypg!JYfy>CXDz-f71{M sr$JkwfjBbMTUAxM@MkmqUpoW%4nfsj#JY?JumAu607*qoM6N<$f=D)juK)l5 literal 0 HcmV?d00001 diff --git a/jadx-gui/src/main/resources/logos/jadx-logo.ico b/jadx-gui/src/main/resources/logos/jadx-logo.ico index 1f02bd689dfd128eea9db30c8a7103fd5a77153b..85786cd6b2b65f29e8097d6fa88bc570dc1a396e 100644 GIT binary patch delta 5019 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 \ No newline at end of file From 52deb48aac643bbcd983fb04bb128b13b4879878 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 16 Mar 2019 20:51:25 +0300 Subject: [PATCH 09/15] fix: move instruction out of try/catch (#468) --- .../java/jadx/core/dex/nodes/InsnNode.java | 20 +++++++++ .../java/jadx/core/dex/nodes/MethodNode.java | 16 +++++-- .../trycatch/TestTryCatchStartOnMove.java | 33 ++++++++++++++ .../trycatch/TestTryCatchStartOnMove.smali | 45 +++++++++++++++++++ 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchStartOnMove.java create mode 100644 jadx-core/src/test/smali/trycatch/TestTryCatchStartOnMove.smali diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index 0d8348dce..f04dd9d12 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -316,4 +316,24 @@ public class InsnNode extends LineAttrNode { } return INSN_CLONER.deepClone(this); } + + public boolean canThrowException() { + switch (getType()) { + case RETURN: + case IF: + case GOTO: + case MOVE: + case MOVE_EXCEPTION: + case NEG: + case CONST: + case CONST_STR: + case CONST_CLASS: + case CMP_L: + case CMP_G: + return false; + + default: + return true; + } + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 969ffd50d..b6067bdef 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -321,16 +321,24 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { int offset = aTry.getStartAddress(); int end = offset + aTry.getInstructionCount() - 1; - InsnNode insn = insnByOffset[offset]; - insn.add(AFlag.TRY_ENTER); + boolean tryBlockStarted = false; + InsnNode insn = null; while (offset <= end && offset >= 0) { insn = insnByOffset[offset]; - catchBlock.addInsn(insn); + if (insn != null) { + if (tryBlockStarted) { + catchBlock.addInsn(insn); + } else if (insn.canThrowException()) { + insn.add(AFlag.TRY_ENTER); + catchBlock.addInsn(insn); + tryBlockStarted = true; + } + } offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset); } if (insnByOffset[end] != null) { insnByOffset[end].add(AFlag.TRY_LEAVE); - } else { + } else if (insn != null) { insn.add(AFlag.TRY_LEAVE); } } diff --git a/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchStartOnMove.java b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchStartOnMove.java new file mode 100644 index 000000000..468a3a243 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/trycatch/TestTryCatchStartOnMove.java @@ -0,0 +1,33 @@ +package jadx.tests.integration.trycatch; + +import org.junit.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.junit.Assert.assertThat; + +public class TestTryCatchStartOnMove extends SmaliTest { + +// private static void test(String s) { +// try { +// call(s); +// } catch (Exception unused) { +// System.out.println("Failed call for " + s); +// } +// } +// +// private static void call(String s) { +// } + + @Test + public void test() { + ClassNode cls = getClassNodeFromSmaliWithPkg("trycatch", "TestTryCatchStartOnMove"); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("try {")); + assertThat(code, containsOne("} catch (Exception e) {")); + assertThat(code, containsOne("System.out.println(\"Failed call for \" + str")); + } +} diff --git a/jadx-core/src/test/smali/trycatch/TestTryCatchStartOnMove.smali b/jadx-core/src/test/smali/trycatch/TestTryCatchStartOnMove.smali new file mode 100644 index 000000000..d09dffbee --- /dev/null +++ b/jadx-core/src/test/smali/trycatch/TestTryCatchStartOnMove.smali @@ -0,0 +1,45 @@ +.class public Ltrycatch/TestTryCatchStartOnMove; +.super Ljava/lang/Object; + + +# direct methods +.method private static test(Ljava/lang/String;)V + .registers 5 + + :try_start + move v3, p0 + invoke-static {v3}, Ltrycatch/TestTryCatchStartOnMove;->call(Ljava/lang/String;)V + :try_end + .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch + + :goto_ret + return-void + + :catch + move-exception v0 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + new-instance v1, Ljava/lang/StringBuilder; + invoke-direct {v1}, Ljava/lang/StringBuilder;->()V + const-string v2, "Failed call for " + invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v1 + invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + move-result-object v1 + invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + move-result-object v1 + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + goto :goto_ret +.end method + + +.method public constructor ()V + .registers 1 + invoke-direct {p0}, Ljadx/tests/api/SmaliTest;->()V + return-void +.end method + + +.method private static call(Ljava/lang/String;)V + .registers 1 + return-void +.end method From 03ae3bcefa531ded62a51f6753d797ac8484c2b6 Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 18 Mar 2019 16:41:45 +0300 Subject: [PATCH 10/15] fix: process field init code in dependency collector (#467) --- .../java/jadx/core/dex/visitors/DependencyCollector.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java index 92cffd845..bc0254389 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java @@ -18,6 +18,7 @@ import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.parser.FieldInitAttr; import jadx.core.utils.exceptions.JadxException; public class DependencyCollector extends AbstractVisitor { @@ -41,6 +42,12 @@ public class DependencyCollector extends AbstractVisitor { } for (FieldNode fieldNode : cls.getFields()) { addDep(dex, depList, fieldNode.getType()); + + // process instructions from field init + FieldInitAttr fieldInitAttr = fieldNode.get(AType.FIELD_INIT); + if (fieldInitAttr != null && fieldInitAttr.getValueType() == FieldInitAttr.InitType.INSN) { + processInsn(dex, depList, fieldInitAttr.getInsn()); + } } // TODO: process annotations and generics for (MethodNode methodNode : cls.getMethods()) { From 3019ee5655a3271bb8197103b9f5aaa85766328f Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 19 Mar 2019 21:22:28 +0300 Subject: [PATCH 11/15] build: check secret variable to skip deploy on pull requests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4b1ee63d8..73e6ca136 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ jobs: include: - stage: deploy-unstable jdk: openjdk8 - if: branch = master AND env(BINTRAY_USER) IS present + if: branch = master AND env(BINTRAY_KEY) IS present script: bash scripts/travis-master.sh - stage: deploy-release From a3ff03c8f3ac25829a44f42cf8ddf8d0544fa411 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Wed, 20 Mar 2019 09:37:57 +0100 Subject: [PATCH 12/15] GUI: disable package if "excluded" --- .../src/main/java/jadx/gui/JadxWrapper.java | 29 +++++- .../java/jadx/gui/settings/JadxSettings.java | 1 + .../jadx/gui/settings/JadxSettingsWindow.java | 4 + .../java/jadx/gui/treemodel/JPackage.java | 45 +++++++-- .../java/jadx/gui/treemodel/JSources.java | 6 +- .../src/main/java/jadx/gui/ui/MainWindow.java | 99 +++++++++++++++---- 6 files changed, 146 insertions(+), 38 deletions(-) diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index 52ec3908b..69bd82107 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -1,12 +1,15 @@ package jadx.gui; -import javax.swing.*; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.ThreadPoolExecutor; import java.util.stream.Collectors; +import javax.swing.ProgressMonitor; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,14 +81,13 @@ public class JadxWrapper { */ public List getIncludedClasses() { List classList = decompiler.getClasses(); - String excludedPackages = settings.getExcludedPackages().trim(); - if (excludedPackages.length() == 0) { + List excludedPackages = getExcludedPackages(); + if (excludedPackages.isEmpty()) { return classList; } - String[] excluded = excludedPackages.split("[ ]+"); return classList.stream().filter(cls -> { - for (String exclude : excluded) { + for (String exclude : excludedPackages) { if (cls.getFullName().startsWith(exclude)) { return false; } @@ -94,6 +96,23 @@ public class JadxWrapper { }).collect(Collectors.toList()); } + public List getExcludedPackages() { + String excludedPackages = settings.getExcludedPackages().trim(); + return Arrays.asList(excludedPackages.split("[ ]+")); + } + + public void addExcludedPackage(String packageToExclude) { + settings.setExcludedPackages(settings.getExcludedPackages() + ' ' + packageToExclude); + settings.sync(); + } + + public void removeExcludedPackage(String packageToRemoveFromExclusion) { + List list = new ArrayList<>(getExcludedPackages()); + list.remove(packageToRemoveFromExclusion); + settings.setExcludedPackages(String.join(" ", list)); + settings.sync(); + } + public List getPackages() { return decompiler.getPackages(); } 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 f0c190229..7a7c04c33 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -333,4 +333,5 @@ public class JadxSettings extends JadxCLIArgs { settingsVersion = CURRENT_SETTINGS_VERSION; sync(); } + } 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 81579a996..c2252a9a7 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettingsWindow.java @@ -247,10 +247,14 @@ public class JadxSettingsWindow extends JDialog { JButton editExcludedPackages = new JButton(NLS.str("preferences.excludedPackages.button")); editExcludedPackages.addActionListener(event -> { + String oldExcludedPackages = settings.getExcludedPackages(); String result = JOptionPane.showInputDialog(this, NLS.str("preferences.excludedPackages.editDialog"), settings.getExcludedPackages()); if (result != null) { settings.setExcludedPackages(result); + if (!oldExcludedPackages.equals(result)) { + needReload(); + } } }); diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java index bfb8ce6bc..8481bbbcb 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java @@ -1,13 +1,16 @@ package jadx.gui.treemodel; -import javax.swing.*; import java.util.ArrayList; import java.util.List; +import javax.swing.Icon; +import javax.swing.ImageIcon; + import org.jetbrains.annotations.NotNull; import jadx.api.JavaClass; import jadx.api.JavaPackage; +import jadx.gui.JadxWrapper; import jadx.gui.utils.Utils; public class JPackage extends JNode implements Comparable { @@ -15,12 +18,16 @@ public class JPackage extends JNode implements Comparable { private static final ImageIcon PACKAGE_ICON = Utils.openIcon("package_obj"); + private final String fullName; private String name; + private boolean enabled; private final List classes; private final List innerPackages = new ArrayList<>(1); - public JPackage(JavaPackage pkg) { + public JPackage(JavaPackage pkg, JadxWrapper wrapper) { + this.fullName = pkg.getName(); this.name = pkg.getName(); + setEnabled(wrapper); List javaClasses = pkg.getClasses(); this.classes = new ArrayList<>(javaClasses.size()); for (JavaClass javaClass : javaClasses) { @@ -29,20 +36,30 @@ public class JPackage extends JNode implements Comparable { update(); } - public JPackage(String name) { + public JPackage(String name, JadxWrapper wrapper) { + this.fullName = name; this.name = name; + setEnabled(wrapper); this.classes = new ArrayList<>(1); } + private void setEnabled(JadxWrapper wrapper) { + List excludedPackages = wrapper.getExcludedPackages(); + this.enabled = excludedPackages.isEmpty() + || excludedPackages.stream().filter(p -> !p.isEmpty()).noneMatch(p -> name.startsWith(p)); + } + public final void update() { removeAllChildren(); - for (JPackage pkg : innerPackages) { - pkg.update(); - add(pkg); - } - for (JClass cls : classes) { - cls.update(); - add(cls); + if (isEnabled()) { + for (JPackage pkg : innerPackages) { + pkg.update(); + add(pkg); + } + for (JClass cls : classes) { + cls.update(); + add(cls); + } } } @@ -51,6 +68,10 @@ public class JPackage extends JNode implements Comparable { return name; } + public String getFullName() { + return fullName; + } + public void setName(String name) { this.name = name; } @@ -108,4 +129,8 @@ public class JPackage extends JNode implements Comparable { public String makeLongString() { return name; } + + public boolean isEnabled() { + return enabled; + } } diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JSources.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JSources.java index d6541fb8c..09a66d91d 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JSources.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JSources.java @@ -33,7 +33,7 @@ public class JSources extends JNode { removeAllChildren(); if (flatPackages) { for (JavaPackage pkg : wrapper.getPackages()) { - add(new JPackage(pkg)); + add(new JPackage(pkg, wrapper)); } } else { // build packages hierarchy @@ -54,7 +54,7 @@ public class JSources extends JNode { List getHierarchyPackages(List packages) { Map pkgMap = new HashMap<>(); for (JavaPackage pkg : packages) { - addPackage(pkgMap, new JPackage(pkg)); + addPackage(pkgMap, new JPackage(pkg, wrapper)); } // merge packages without classes boolean repeat; @@ -114,7 +114,7 @@ public class JSources extends JNode { pkg.setName(shortName); JPackage prevPkg = pkgs.get(prevPart); if (prevPkg == null) { - prevPkg = new JPackage(prevPart); + prevPkg = new JPackage(prevPart, wrapper); addPackage(pkgs, prevPkg); } prevPkg.getInnerPackages().add(pkg); 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 4783e984f..608e50c66 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -1,6 +1,47 @@ package jadx.gui.ui; -import javax.swing.*; +import static javax.swing.KeyStroke.getKeyStroke; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.DisplayMode; +import java.awt.Font; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.FileInputStream; +import java.util.Arrays; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.JTree; +import javax.swing.ProgressMonitor; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.event.TreeExpansionEvent; @@ -12,22 +53,6 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; -import java.awt.*; -import java.awt.dnd.DnDConstants; -import java.awt.dnd.DropTarget; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.File; -import java.io.FileInputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; import org.fife.ui.rsyntaxtextarea.Theme; import org.slf4j.Logger; @@ -46,6 +71,7 @@ import jadx.gui.treemodel.JCertificate; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JLoadableNode; import jadx.gui.treemodel.JNode; +import jadx.gui.treemodel.JPackage; import jadx.gui.treemodel.JResource; import jadx.gui.treemodel.JRoot; import jadx.gui.update.JadxUpdate; @@ -57,8 +83,6 @@ import jadx.gui.utils.Link; import jadx.gui.utils.NLS; import jadx.gui.utils.Utils; -import static javax.swing.KeyStroke.getKeyStroke; - @SuppressWarnings("serial") public class MainWindow extends JFrame { private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class); @@ -315,6 +339,14 @@ public class MainWindow extends JFrame { } } + private void treeRightClickAction(MouseEvent e) { + Object obj = tree.getLastSelectedPathComponent(); + if (obj instanceof JPackage) { + JPackagePopUp menu = new JPackagePopUp((JPackage) obj); + menu.show(e.getComponent(), e.getX(), e.getY()); + } + } + private void syncWithEditor() { ContentPanel selectedContentPanel = tabbedPane.getSelectedCodePanel(); if (selectedContentPanel == null) { @@ -574,7 +606,12 @@ public class MainWindow extends JFrame { tree.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - treeClickAction(); + if (SwingUtilities.isRightMouseButton(e)) { + treeRightClickAction(e); + } + else { + treeClickAction(); + } } }); tree.addKeyListener(new KeyAdapter() { @@ -594,6 +631,9 @@ public class MainWindow extends JFrame { if (value instanceof JNode) { setIcon(((JNode) value).getIcon()); } + if (value instanceof JPackage) { + setEnabled(((JPackage) value).isEnabled()); + } return c; } }); @@ -740,4 +780,23 @@ public class MainWindow extends JFrame { public void menuCanceled(MenuEvent e) { } } + + private class JPackagePopUp extends JPopupMenu { + JMenuItem excludeItem = new JCheckBoxMenuItem("Exclude"); + + public JPackagePopUp(JPackage pkg) { + excludeItem.setSelected(!pkg.isEnabled()); + add(excludeItem); + excludeItem.addItemListener(e -> { + String fullName = pkg.getFullName(); + if (excludeItem.isSelected()) { + wrapper.addExcludedPackage(fullName); + } + else { + wrapper.removeExcludedPackage(fullName); + } + reOpenFile(); + }); + } + } } From 2b242b9109bf97969432776e8f9628262e62402f Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Wed, 20 Mar 2019 11:28:58 +0100 Subject: [PATCH 13/15] style: remove unused imports (PR #475) --- .../test/java/jadx/tests/api/compiler/ClassFileManager.java | 2 -- .../jadx/tests/integration/debuginfo/TestVariablesNames.java | 2 -- .../inner/TestInnerClassFakeSyntheticConstructor.java | 1 - .../integration/inner/TestInnerClassSyntheticConstructor.java | 4 ---- .../java/jadx/tests/integration/loops/TestEndlessLoop.java | 1 - .../tests/integration/switches/TestSwitchReturnFromCase2.java | 2 -- 6 files changed, 12 deletions(-) diff --git a/jadx-core/src/test/java/jadx/tests/api/compiler/ClassFileManager.java b/jadx-core/src/test/java/jadx/tests/api/compiler/ClassFileManager.java index 199d6e250..d07e1511a 100644 --- a/jadx-core/src/test/java/jadx/tests/api/compiler/ClassFileManager.java +++ b/jadx-core/src/test/java/jadx/tests/api/compiler/ClassFileManager.java @@ -6,10 +6,8 @@ import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import java.io.IOException; import java.security.SecureClassLoader; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import static javax.tools.JavaFileObject.Kind; diff --git a/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java b/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java index e55066091..098c4e5c0 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java +++ b/jadx-core/src/test/java/jadx/tests/integration/debuginfo/TestVariablesNames.java @@ -3,10 +3,8 @@ package jadx.tests.integration.debuginfo; import org.junit.Test; import jadx.core.dex.nodes.ClassNode; -import jadx.tests.api.IntegrationTest; import jadx.tests.api.SmaliTest; -import static jadx.tests.api.utils.JadxMatchers.containsLines; import static jadx.tests.api.utils.JadxMatchers.containsOne; import static org.junit.Assert.assertThat; diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassFakeSyntheticConstructor.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassFakeSyntheticConstructor.java index 1560806a6..1a9a88021 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassFakeSyntheticConstructor.java +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassFakeSyntheticConstructor.java @@ -3,7 +3,6 @@ package jadx.tests.integration.inner; import org.junit.Test; import jadx.core.dex.nodes.ClassNode; -import jadx.tests.api.IntegrationTest; import jadx.tests.api.SmaliTest; import static jadx.tests.api.utils.JadxMatchers.containsOne; diff --git a/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassSyntheticConstructor.java b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassSyntheticConstructor.java index 6deb70ef8..7431c5eb0 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassSyntheticConstructor.java +++ b/jadx-core/src/test/java/jadx/tests/integration/inner/TestInnerClassSyntheticConstructor.java @@ -3,10 +3,6 @@ package jadx.tests.integration.inner; import org.junit.Test; import jadx.tests.api.IntegrationTest; -import jadx.tests.api.SmaliTest; - -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; public class TestInnerClassSyntheticConstructor extends IntegrationTest { diff --git a/jadx-core/src/test/java/jadx/tests/integration/loops/TestEndlessLoop.java b/jadx-core/src/test/java/jadx/tests/integration/loops/TestEndlessLoop.java index 28dedc5e6..cc463ed7f 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/loops/TestEndlessLoop.java +++ b/jadx-core/src/test/java/jadx/tests/integration/loops/TestEndlessLoop.java @@ -5,7 +5,6 @@ import org.junit.Test; import jadx.core.dex.nodes.ClassNode; import jadx.tests.api.IntegrationTest; -import static jadx.tests.api.utils.JadxMatchers.containsOne; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertThat; diff --git a/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase2.java b/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase2.java index 63df67722..383820ade 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase2.java @@ -4,9 +4,7 @@ import jadx.core.dex.nodes.ClassNode; import jadx.tests.api.IntegrationTest; import org.junit.Test; -import static jadx.tests.api.utils.JadxMatchers.containsOne; import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; From 6d052d39ad05eec54e944169fe5640507d3b4c9a Mon Sep 17 00:00:00 2001 From: Skylot Date: Wed, 20 Mar 2019 13:59:19 +0300 Subject: [PATCH 14/15] build: check repo name and build type to skip deploy on PR and forks --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 73e6ca136..b702ae7b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,10 +31,10 @@ jobs: include: - stage: deploy-unstable jdk: openjdk8 - if: branch = master AND env(BINTRAY_KEY) IS present + if: branch = master AND repo = env(MAIN_REPO) AND type = push script: bash scripts/travis-master.sh - stage: deploy-release jdk: openjdk8 - if: branch = release AND env(GH_TOKEN) IS present + if: branch = release AND repo = env(MAIN_REPO) AND type = push script: bash scripts/travis-release.sh From 9797fe5b8107c9378cd91b02df61024b15607139 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Wed, 20 Mar 2019 12:04:51 +0100 Subject: [PATCH 15/15] fix(gui): sort resources according to their type, then name (PR #479) --- jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java index 753af2523..59a82b20b 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java @@ -3,6 +3,7 @@ package jadx.gui.treemodel; import javax.swing.*; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; @@ -68,6 +69,14 @@ public class JResource extends JLoadableNode implements Comparable { } } else { removeAllChildren(); + + Comparator typeComparator + = (r1, r2) -> r1.type.ordinal() - r2.type.ordinal(); + Comparator nameComparator + = Comparator.comparing(JResource::getName, String.CASE_INSENSITIVE_ORDER); + + files.sort(typeComparator.thenComparing(nameComparator)); + for (JResource res : files) { res.update(); add(res);