Commit 365c1faf authored by Skylot's avatar Skylot

Merge branch 'master' into type-inference-wip

parents cf79a519 9797fe5b
...@@ -12,8 +12,7 @@ before_install: ...@@ -12,8 +12,7 @@ before_install:
- chmod +x gradlew - chmod +x gradlew
# override install to skip 'gradle assemble' # override install to skip 'gradle assemble'
install: install: true
- true
env: env:
global: global:
...@@ -21,34 +20,21 @@ env: ...@@ -21,34 +20,21 @@ env:
- JADX_LAST_TAG=$(git describe --abbrev=0 --tags) - JADX_LAST_TAG=$(git describe --abbrev=0 --tags)
- JADX_VERSION="${JADX_LAST_TAG:1}-b$TRAVIS_BUILD_NUMBER-$(git rev-parse --short HEAD)" - 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: include:
- env: JDK=oracle-8 - stage: deploy-unstable
jdk: oraclejdk8 jdk: openjdk8
- env: JDK=openjdk11 if: branch = master AND repo = env(MAIN_REPO) AND type = push
jdk: openjdk11
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 script: bash scripts/travis-master.sh
- provider: script - stage: deploy-release
skip_cleanup: true jdk: openjdk8
on: if: branch = release AND repo = env(MAIN_REPO) AND type = push
branch: release
tags: false
condition: $JDK = oracle-8
script: bash scripts/travis-release.sh script: bash scripts/travis-release.sh
notifications:
email:
- skylot@gmail.com
plugins { plugins {
id 'org.sonarqube' version '2.7' 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" ext.jadxVersion = System.getenv('JADX_VERSION') ?: "dev"
...@@ -35,12 +35,12 @@ allprojects { ...@@ -35,12 +35,12 @@ allprojects {
} }
dependencies { 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 'ch.qos.logback:logback-classic:1.2.3'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:2.1' 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' testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
} }
......
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
...@@ -5,13 +5,17 @@ dependencies { ...@@ -5,13 +5,17 @@ dependencies {
compile files('lib/dx-1.16.jar') compile files('lib/dx-1.16.jar')
compile 'commons-io:commons-io:2.6' compile 'commons-io:commons-io:2.6'
compile 'org.ow2.asm:asm:7.0' compile 'org.ow2.asm:asm:7.1'
compile 'org.jetbrains:annotations:16.0.3' compile 'org.jetbrains:annotations:17.0.0'
compile 'uk.com.robust-it:cloning:1.9.11' compile 'uk.com.robust-it:cloning:1.9.12'
testCompile 'org.smali:smali:2.2.5' testCompile 'org.smali:smali:2.2.6'
testCompile 'org.smali:baksmali:2.2.5' testCompile 'org.smali:baksmali:2.2.6'
testCompile 'org.apache.commons:commons-lang3:3.8.1' 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'
} }
...@@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory; ...@@ -17,7 +17,6 @@ import org.slf4j.LoggerFactory;
import jadx.core.Jadx; import jadx.core.Jadx;
import jadx.core.ProcessClass; import jadx.core.ProcessClass;
import jadx.core.codegen.CodeGen;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
...@@ -59,7 +58,6 @@ public final class JadxDecompiler { ...@@ -59,7 +58,6 @@ public final class JadxDecompiler {
private RootNode root; private RootNode root;
private List<IDexTreeVisitor> passes; private List<IDexTreeVisitor> passes;
private CodeGen codeGen;
private List<JavaClass> classes; private List<JavaClass> classes;
private List<ResourceFile> resources; private List<ResourceFile> resources;
...@@ -97,7 +95,6 @@ public final class JadxDecompiler { ...@@ -97,7 +95,6 @@ public final class JadxDecompiler {
void init() { void init() {
this.passes = Jadx.getPassesList(args); this.passes = Jadx.getPassesList(args);
this.codeGen = new CodeGen();
} }
void reset() { void reset() {
...@@ -106,7 +103,6 @@ public final class JadxDecompiler { ...@@ -106,7 +103,6 @@ public final class JadxDecompiler {
xmlParser = null; xmlParser = null;
root = null; root = null;
passes = null; passes = null;
codeGen = null;
} }
public static String getVersion() { public static String getVersion() {
...@@ -215,10 +211,12 @@ public final class JadxDecompiler { ...@@ -215,10 +211,12 @@ public final class JadxDecompiler {
List<JavaClass> clsList = new ArrayList<>(classNodeList.size()); List<JavaClass> clsList = new ArrayList<>(classNodeList.size());
classesMap.clear(); classesMap.clear();
for (ClassNode classNode : classNodeList) { for (ClassNode classNode : classNodeList) {
if (!classNode.contains(AFlag.DONT_GENERATE)) {
JavaClass javaClass = new JavaClass(classNode, this); JavaClass javaClass = new JavaClass(classNode, this);
clsList.add(javaClass); clsList.add(javaClass);
classesMap.put(classNode, javaClass); classesMap.put(classNode, javaClass);
} }
}
classes = Collections.unmodifiableList(clsList); classes = Collections.unmodifiableList(clsList);
} }
return classes; return classes;
...@@ -289,7 +287,7 @@ public final class JadxDecompiler { ...@@ -289,7 +287,7 @@ public final class JadxDecompiler {
} }
void processClass(ClassNode cls) { void processClass(ClassNode cls) {
ProcessClass.process(cls, passes, codeGen); ProcessClass.process(cls, passes, true);
} }
RootNode getRoot() { RootNode getRoot() {
......
...@@ -2,8 +2,6 @@ package jadx.core; ...@@ -2,8 +2,6 @@ package jadx.core;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.Nullable;
import jadx.core.codegen.CodeGen; import jadx.core.codegen.CodeGen;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.DepthTraversal;
...@@ -19,8 +17,8 @@ public final class ProcessClass { ...@@ -19,8 +17,8 @@ public final class ProcessClass {
private ProcessClass() { private ProcessClass() {
} }
public static void process(ClassNode cls, List<IDexTreeVisitor> passes, @Nullable CodeGen codeGen) { public static void process(ClassNode cls, List<IDexTreeVisitor> passes, boolean generateCode) {
if (codeGen == null && cls.getState() == PROCESSED) { if (!generateCode && cls.getState() == PROCESSED) {
return; return;
} }
synchronized (getSyncObj(cls)) { synchronized (getSyncObj(cls)) {
...@@ -33,9 +31,9 @@ public final class ProcessClass { ...@@ -33,9 +31,9 @@ public final class ProcessClass {
} }
cls.setState(PROCESSED); cls.setState(PROCESSED);
} }
if (cls.getState() == PROCESSED && codeGen != null) { if (cls.getState() == PROCESSED && generateCode) {
processDependencies(cls, passes); processDependencies(cls, passes);
codeGen.visit(cls); CodeGen.generate(cls);
} }
} catch (Exception e) { } catch (Exception e) {
ErrorsCounter.classError(cls, e.getClass().getSimpleName(), e); ErrorsCounter.classError(cls, e.getClass().getSimpleName(), e);
...@@ -48,6 +46,6 @@ public final class ProcessClass { ...@@ -48,6 +46,6 @@ public final class ProcessClass {
} }
private static void processDependencies(ClassNode cls, List<IDexTreeVisitor> passes) { private static void processDependencies(ClassNode cls, List<IDexTreeVisitor> passes) {
cls.getDependencies().forEach(depCls -> process(depCls, passes, null)); cls.getDependencies().forEach(depCls -> process(depCls, passes, false));
} }
} }
...@@ -97,7 +97,7 @@ public class ClassGen { ...@@ -97,7 +97,7 @@ public class ClassGen {
imports.clear(); imports.clear();
} }
clsCode.add(clsBody); clsCode.add(clsBody);
return clsCode; return clsCode.finish();
} }
public void addClassCode(CodeWriter code) throws CodegenException { public void addClassCode(CodeWriter code) throws CodegenException {
......
package jadx.core.codegen; package jadx.core.codegen;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
public class CodeGen { public class CodeGen {
public boolean visit(ClassNode cls) throws CodegenException { 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()); ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
CodeWriter clsCode = clsGen.makeClass(); cls.setCode(clsGen.makeClass());
clsCode.finish(); }
cls.setCode(clsCode); }
return false;
private CodeGen() {
} }
} }
...@@ -24,6 +24,8 @@ public class CodeWriter { ...@@ -24,6 +24,8 @@ public class CodeWriter {
public static final String NL = System.getProperty("line.separator"); public static final String NL = System.getProperty("line.separator");
public static final String INDENT_STR = " "; 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 boolean ADD_LINE_NUMBERS = false;
private static final String[] INDENT_CACHE = { private static final String[] INDENT_CACHE = {
...@@ -250,7 +252,7 @@ public class CodeWriter { ...@@ -250,7 +252,7 @@ public class CodeWriter {
return lineMap; return lineMap;
} }
public void finish() { public CodeWriter finish() {
removeFirstEmptyLine(); removeFirstEmptyLine();
buf.trimToSize(); buf.trimToSize();
code = buf.toString(); code = buf.toString();
...@@ -266,11 +268,12 @@ public class CodeWriter { ...@@ -266,11 +268,12 @@ public class CodeWriter {
it.remove(); it.remove();
} }
} }
return this;
} }
private void removeFirstEmptyLine() { private void removeFirstEmptyLine() {
int len = NL.length(); 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); buf.delete(0, len);
} }
} }
......
...@@ -48,7 +48,6 @@ import jadx.core.dex.nodes.FieldNode; ...@@ -48,7 +48,6 @@ import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils; import jadx.core.utils.RegionUtils;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
...@@ -549,7 +548,7 @@ public class InsnGen { ...@@ -549,7 +548,7 @@ public class InsnGen {
throws CodegenException { throws CodegenException {
ClassNode cls = mth.dex().resolveClass(insn.getClassType()); ClassNode cls = mth.dex().resolveClass(insn.getClassType());
if (cls != null && cls.contains(AFlag.ANONYMOUS_CLASS) && !fallback) { if (cls != null && cls.contains(AFlag.ANONYMOUS_CLASS) && !fallback) {
inlineAnonymousConstr(code, cls, insn); inlineAnonymousConstructor(code, cls, insn);
return; return;
} }
if (insn.isSelf()) { if (insn.isSelf()) {
...@@ -567,20 +566,14 @@ public class InsnGen { ...@@ -567,20 +566,14 @@ public class InsnGen {
generateMethodArguments(code, insn, 0, callMth); generateMethodArguments(code, insn, 0, callMth);
} }
private void inlineAnonymousConstr(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException { private void inlineAnonymousConstructor(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException {
// anonymous class construction cls.add(AFlag.DONT_GENERATE);
if (cls.contains(AFlag.DONT_GENERATE)) {
code.add("/* anonymous class already generated */");
ErrorsCounter.methodWarn(mth, "Anonymous class already generated: " + cls);
return;
}
ArgType parent; ArgType parent;
if (cls.getInterfaces().size() == 1) { if (cls.getInterfaces().size() == 1) {
parent = cls.getInterfaces().get(0); parent = cls.getInterfaces().get(0);
} else { } else {
parent = cls.getSuperClass(); parent = cls.getSuperClass();
} }
cls.add(AFlag.DONT_GENERATE);
MethodNode defCtr = cls.getDefaultConstructor(); MethodNode defCtr = cls.getDefaultConstructor();
if (defCtr != null) { if (defCtr != null) {
if (RegionUtils.notEmpty(defCtr.getRegion())) { if (RegionUtils.notEmpty(defCtr.getRegion())) {
......
...@@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory; ...@@ -19,6 +19,7 @@ import org.slf4j.LoggerFactory;
import jadx.core.Consts; import jadx.core.Consts;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.annotations.Annotation; import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.dex.attributes.nodes.SourceFileAttr; import jadx.core.dex.attributes.nodes.SourceFileAttr;
...@@ -123,7 +124,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { ...@@ -123,7 +124,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
accFlagsValue = cls.getAccessFlags(); accFlagsValue = cls.getAccessFlags();
} }
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS); this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
markAnonymousClass(this);
buildCache(); buildCache();
} catch (Exception e) { } catch (Exception e) {
throw new JadxRuntimeException("Error decode class: " + clsInfo, e); throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
...@@ -394,6 +395,29 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode { ...@@ -394,6 +395,29 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
&& getDefaultConstructor() != null; && 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 @Nullable
public MethodNode getClassInitMth() { public MethodNode getClassInitMth() {
return searchMethodByName("<clinit>()V"); return searchMethodByName("<clinit>()V");
......
...@@ -327,4 +327,24 @@ public class InsnNode extends LineAttrNode { ...@@ -327,4 +327,24 @@ public class InsnNode extends LineAttrNode {
} }
return INSN_CLONER.deepClone(this); 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;
}
}
} }
...@@ -329,16 +329,24 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode { ...@@ -329,16 +329,24 @@ public class MethodNode extends LineAttrNode implements ILoadable, ICodeNode {
int offset = aTry.getStartAddress(); int offset = aTry.getStartAddress();
int end = offset + aTry.getInstructionCount() - 1; int end = offset + aTry.getInstructionCount() - 1;
InsnNode insn = insnByOffset[offset]; boolean tryBlockStarted = false;
insn.add(AFlag.TRY_ENTER); InsnNode insn = null;
while (offset <= end && offset >= 0) { while (offset <= end && offset >= 0) {
insn = insnByOffset[offset]; insn = insnByOffset[offset];
if (insn != null) {
if (tryBlockStarted) {
catchBlock.addInsn(insn);
} else if (insn.canThrowException()) {
insn.add(AFlag.TRY_ENTER);
catchBlock.addInsn(insn); catchBlock.addInsn(insn);
tryBlockStarted = true;
}
}
offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset); offset = InsnDecoder.getNextInsnOffset(insnByOffset, offset);
} }
if (insnByOffset[end] != null) { if (insnByOffset[end] != null) {
insnByOffset[end].add(AFlag.TRY_LEAVE); insnByOffset[end].add(AFlag.TRY_LEAVE);
} else { } else if (insn != null) {
insn.add(AFlag.TRY_LEAVE); insn.add(AFlag.TRY_LEAVE);
} }
} }
......
...@@ -51,11 +51,10 @@ public class ClassModifier extends AbstractVisitor { ...@@ -51,11 +51,10 @@ public class ClassModifier extends AbstractVisitor {
cls.add(AFlag.DONT_GENERATE); cls.add(AFlag.DONT_GENERATE);
return false; return false;
} }
markAnonymousClass(cls);
removeSyntheticFields(cls); removeSyntheticFields(cls);
cls.getMethods().forEach(mth -> removeSyntheticMethods(cls, mth)); cls.getMethods().forEach(ClassModifier::removeSyntheticMethods);
cls.getMethods().forEach(ClassModifier::removeEmptyMethods); cls.getMethods().forEach(ClassModifier::removeEmptyMethods);
markAnonymousClass(cls);
return false; return false;
} }
...@@ -69,20 +68,26 @@ public class ClassModifier extends AbstractVisitor { ...@@ -69,20 +68,26 @@ public class ClassModifier extends AbstractVisitor {
private void markAnonymousClass(ClassNode cls) { private void markAnonymousClass(ClassNode cls) {
if (cls.isAnonymous()) { if (cls.isAnonymous()) {
cls.add(AFlag.ANONYMOUS_CLASS); 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) { private static void removeSyntheticFields(ClassNode cls) {
if (!cls.getClassInfo().isInner() || cls.getAccessFlags().isStatic()) { if (cls.getAccessFlags().isStatic()) {
return; return;
} }
// remove fields if it is synthetic and type is a outer class boolean inline = cls.contains(AFlag.ANONYMOUS_CLASS);
if (inline || cls.getClassInfo().isInner()) {
for (FieldNode field : cls.getFields()) { for (FieldNode field : cls.getFields()) {
if (field.getAccessFlags().isSynthetic() && field.getType().isObject()) { if (field.getAccessFlags().isSynthetic() && field.getType().isObject()) {
ClassInfo clsInfo = ClassInfo.fromType(cls.root(), field.getType()); ClassInfo clsInfo = ClassInfo.fromType(cls.root(), field.getType());
ClassNode fieldsCls = cls.dex().resolveClass(clsInfo); ClassNode fieldsCls = cls.dex().resolveClass(clsInfo);
ClassInfo parentClass = cls.getClassInfo().getParentClass(); ClassInfo parentClass = cls.getClassInfo().getParentClass();
if (fieldsCls != null && parentClass.equals(fieldsCls.getClassInfo())) { if (fieldsCls != null
&& (inline || parentClass.equals(fieldsCls.getClassInfo()))) {
int found = 0; int found = 0;
for (MethodNode mth : cls.getMethods()) { for (MethodNode mth : cls.getMethods()) {
if (removeFieldUsageFromConstructor(mth, field, fieldsCls)) { if (removeFieldUsageFromConstructor(mth, field, fieldsCls)) {
...@@ -90,13 +95,14 @@ public class ClassModifier extends AbstractVisitor { ...@@ -90,13 +95,14 @@ public class ClassModifier extends AbstractVisitor {
} }
} }
if (found != 0) { if (found != 0) {
field.addAttr(new FieldReplaceAttr(parentClass)); field.addAttr(new FieldReplaceAttr(fieldsCls.getClassInfo()));
field.add(AFlag.DONT_GENERATE); field.add(AFlag.DONT_GENERATE);
} }
} }
} }
} }
} }
}
private static boolean removeFieldUsageFromConstructor(MethodNode mth, FieldNode field, ClassNode fieldsCls) { private static boolean removeFieldUsageFromConstructor(MethodNode mth, FieldNode field, ClassNode fieldsCls) {
if (mth.isNoCode() || !mth.getAccessFlags().isConstructor()) { if (mth.isNoCode() || !mth.getAccessFlags().isConstructor()) {
...@@ -137,7 +143,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -137,7 +143,7 @@ public class ClassModifier extends AbstractVisitor {
return true; return true;
} }
private static void removeSyntheticMethods(ClassNode cls, MethodNode mth) { private static void removeSyntheticMethods(MethodNode mth) {
if (mth.isNoCode()) { if (mth.isNoCode()) {
return; return;
} }
...@@ -145,6 +151,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -145,6 +151,7 @@ public class ClassModifier extends AbstractVisitor {
if (!af.isSynthetic()) { if (!af.isSynthetic()) {
return; return;
} }
ClassNode cls = mth.getParentClass();
if (removeBridgeMethod(cls, mth)) { if (removeBridgeMethod(cls, mth)) {
if (Consts.DEBUG) { if (Consts.DEBUG) {
mth.addAttr(AType.COMMENTS, "Removed as synthetic bridge method"); mth.addAttr(AType.COMMENTS, "Removed as synthetic bridge method");
......
...@@ -18,6 +18,7 @@ import jadx.core.dex.nodes.DexNode; ...@@ -18,6 +18,7 @@ import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.parser.FieldInitAttr;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
public class DependencyCollector extends AbstractVisitor { public class DependencyCollector extends AbstractVisitor {
...@@ -41,6 +42,12 @@ public class DependencyCollector extends AbstractVisitor { ...@@ -41,6 +42,12 @@ public class DependencyCollector extends AbstractVisitor {
} }
for (FieldNode fieldNode : cls.getFields()) { for (FieldNode fieldNode : cls.getFields()) {
addDep(dex, depList, fieldNode.getType()); 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 // TODO: process annotations and generics
for (MethodNode methodNode : cls.getMethods()) { for (MethodNode methodNode : cls.getMethods()) {
......
...@@ -5,6 +5,8 @@ import java.util.Collections; ...@@ -5,6 +5,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.BlockNode;
...@@ -136,7 +138,10 @@ public class RegionUtils { ...@@ -136,7 +138,10 @@ public class RegionUtils {
return !notEmpty(container); 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) { if (container instanceof IBlock) {
return !((IBlock) container).getInstructions().isEmpty(); return !((IBlock) container).getInstructions().isEmpty();
} else if (container instanceof IRegion) { } else if (container instanceof IRegion) {
......
package jadx.core.utils.android; package jadx.core.utils.android;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import com.android.dx.rop.code.AccessFlags; import com.android.dx.rop.code.AccessFlags;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jadx.core.codegen.ClassGen; import jadx.core.codegen.ClassGen;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.NameMapper;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.ConstStorage;
import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
...@@ -86,6 +91,7 @@ public class AndroidResourcesUtils { ...@@ -86,6 +91,7 @@ public class AndroidResourcesUtils {
} }
private static void addResourceFields(ClassNode resCls, ResourceStorage resStorage, boolean rClsExists) { private static void addResourceFields(ClassNode resCls, ResourceStorage resStorage, boolean rClsExists) {
Map<Integer, FieldNode> resFieldsMap = fillResFieldsMap(resCls);
Map<String, ClassNode> innerClsMap = new TreeMap<>(); Map<String, ClassNode> innerClsMap = new TreeMap<>();
if (rClsExists) { if (rClsExists) {
for (ClassNode innerClass : resCls.getInnerClasses()) { for (ClassNode innerClass : resCls.getInnerClasses()) {
...@@ -93,18 +99,20 @@ public class AndroidResourcesUtils { ...@@ -93,18 +99,20 @@ public class AndroidResourcesUtils {
} }
} }
for (ResourceEntry resource : resStorage.getResources()) { for (ResourceEntry resource : resStorage.getResources()) {
ClassNode typeCls = innerClsMap.computeIfAbsent(resource.getTypeName(), name -> { final String resTypeName = resource.getTypeName();
ClassNode newTypeCls = new ClassNode(resCls.dex(), resCls.getFullName() + "$" + name, ClassNode typeCls = innerClsMap.computeIfAbsent(
AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL); resTypeName,
resCls.addInnerClass(newTypeCls); name -> addClassForResType(resCls, rClsExists, name)
if (rClsExists) { );
newTypeCls.addAttr(AType.COMMENTS, "added by JADX"); final String resName;
} if ("style".equals(resTypeName)) {
return newTypeCls; resName = resource.getKeyName().replace('.', '_');
}); } else {
FieldNode rField = typeCls.searchFieldByName(resource.getKeyName()); resName = resource.getKeyName();
}
FieldNode rField = typeCls.searchFieldByName(resName);
if (rField == null) { 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 = new FieldNode(typeCls, rFieldInfo, AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC | AccessFlags.ACC_FINAL);
rField.addAttr(FieldInitAttr.constValue(resource.getId())); rField.addAttr(FieldInitAttr.constValue(resource.getId()));
typeCls.getFields().add(rField); typeCls.getFields().add(rField);
...@@ -112,6 +120,42 @@ public class AndroidResourcesUtils { ...@@ -112,6 +120,42 @@ public class AndroidResourcesUtils {
rField.addAttr(AType.COMMENTS, "added by JADX"); 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<Integer, FieldNode> fillResFieldsMap(ClassNode resCls) {
Map<Integer, FieldNode> resFieldsMap = new HashMap<>();
ConstStorage constStorage = resCls.root().getConstValues();
Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields();
for (Map.Entry<Object, FieldNode> 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;
}
} }
...@@ -59,8 +59,8 @@ public class InputFile { ...@@ -59,8 +59,8 @@ public class InputFile {
loadFromZip(".dex"); loadFromZip(".dex");
return; return;
} }
if (fileName.endsWith(".jar")) { if (fileName.endsWith(".jar") || fileName.endsWith(".aar")) {
// check if jar contains '.dex' files // check if jar/aar contains '.dex' files
if (loadFromZip(".dex")) { if (loadFromZip(".dex")) {
return; return;
} }
......
...@@ -15,8 +15,6 @@ import org.slf4j.LoggerFactory; ...@@ -15,8 +15,6 @@ import org.slf4j.LoggerFactory;
import jadx.api.ResourcesLoader; import jadx.api.ResourcesLoader;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.info.ConstStorage; 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.dex.nodes.RootNode;
import jadx.core.utils.StringUtils; import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
...@@ -40,7 +38,6 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -40,7 +38,6 @@ public class BinaryXMLParser extends CommonBinaryParser {
private static final boolean ATTR_NEW_LINE = false; private static final boolean ATTR_NEW_LINE = false;
private final Map<Integer, String> styleMap = new HashMap<>(); private final Map<Integer, String> styleMap = new HashMap<>();
private final Map<Integer, FieldNode> localStyleMap = new HashMap<>();
private final Map<Integer, String> resNames; private final Map<Integer, String> resNames;
private final Map<String, String> nsMap = new HashMap<>(); private final Map<String, String> nsMap = new HashMap<>();
private Set<String> nsMapGenerated; private Set<String> nsMapGenerated;
...@@ -63,16 +60,7 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -63,16 +60,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
this.rootNode = rootNode; this.rootNode = rootNode;
try { try {
readAndroidRStyleClass(); readAndroidRStyleClass();
// add application constants
ConstStorage constStorage = rootNode.getConstValues(); ConstStorage constStorage = rootNode.getConstValues();
Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields();
for (Map.Entry<Object, FieldNode> 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(); resNames = constStorage.getResourcesNames();
} catch (Exception e) { } catch (Exception e) {
throw new JadxRuntimeException("BinaryXMLParser init error", e); throw new JadxRuntimeException("BinaryXMLParser init error", e);
...@@ -381,22 +369,12 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -381,22 +369,12 @@ public class BinaryXMLParser extends CommonBinaryParser {
private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData, private void decodeAttribute(int attributeNS, int attrValDataType, int attrValData,
String shortNsName, String attrName) { String shortNsName, String attrName) {
if (attrValDataType == TYPE_REFERENCE) { if (attrValDataType == TYPE_REFERENCE) {
// reference custom processing // reference custom processing
String name = styleMap.get(attrValData); String name = styleMap.get(attrValData);
if (name != null) { if (name != null) {
writer.add("@style/").add(name.replaceAll("_", ".")); writer.add("@style/").add(name.replaceAll("_", "."));
} else { } else {
FieldNode field = localStyleMap.get(attrValData);
if (field != null) {
String cls = field.getParentClass().getShortName().toLowerCase();
writer.add("@");
if ("id".equals(cls)) {
writer.add('+');
}
writer.add(cls).add("/").add(field.getName());
} else {
String resName = resNames.get(attrValData); String resName = resNames.get(attrValData);
if (resName != null) { if (resName != null) {
writer.add("@"); writer.add("@");
...@@ -415,7 +393,6 @@ public class BinaryXMLParser extends CommonBinaryParser { ...@@ -415,7 +393,6 @@ public class BinaryXMLParser extends CommonBinaryParser {
} }
} }
} }
}
} else { } else {
String str = valuesParser.decodeValue(attrValDataType, attrValData); String str = valuesParser.decodeValue(attrValDataType, attrValData);
memorizePackageName(attrName, str); memorizePackageName(attrName, str);
......
...@@ -140,7 +140,7 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -140,7 +140,7 @@ public abstract class IntegrationTest extends TestUtils {
protected void decompile(JadxDecompiler jadx, ClassNode cls) { protected void decompile(JadxDecompiler jadx, ClassNode cls) {
List<IDexTreeVisitor> passes = JadxInternalAccess.getPassList(jadx); List<IDexTreeVisitor> passes = JadxInternalAccess.getPassList(jadx);
ProcessClass.process(cls, passes, new CodeGen()); ProcessClass.process(cls, passes, true);
} }
protected void decompileWithoutUnload(JadxDecompiler jadx, ClassNode cls) { protected void decompileWithoutUnload(JadxDecompiler jadx, ClassNode cls) {
...@@ -154,7 +154,7 @@ public abstract class IntegrationTest extends TestUtils { ...@@ -154,7 +154,7 @@ public abstract class IntegrationTest extends TestUtils {
protected void generateClsCode(ClassNode cls) { protected void generateClsCode(ClassNode cls) {
try { try {
new CodeGen().visit(cls); CodeGen.generate(cls);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
fail(e.getMessage()); fail(e.getMessage());
......
...@@ -6,10 +6,8 @@ import javax.tools.JavaFileObject; ...@@ -6,10 +6,8 @@ import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager; import javax.tools.StandardJavaFileManager;
import java.io.IOException; import java.io.IOException;
import java.security.SecureClassLoader; import java.security.SecureClassLoader;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static javax.tools.JavaFileObject.Kind; import static javax.tools.JavaFileObject.Kind;
......
...@@ -15,12 +15,11 @@ import jadx.api.JadxArgs; ...@@ -15,12 +15,11 @@ import jadx.api.JadxArgs;
import jadx.api.JadxDecompiler; import jadx.api.JadxDecompiler;
import jadx.api.JadxInternalAccess; import jadx.api.JadxInternalAccess;
import jadx.api.JavaClass; import jadx.api.JavaClass;
import jadx.core.codegen.CodeGen; import jadx.core.ProcessClass;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.DepthTraversal;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.tests.api.IntegrationTest; import jadx.tests.api.IntegrationTest;
...@@ -99,15 +98,10 @@ public abstract class BaseExternalTest extends IntegrationTest { ...@@ -99,15 +98,10 @@ public abstract class BaseExternalTest extends IntegrationTest {
if (!decompile) { if (!decompile) {
return false; return false;
} }
// ProcessClass.process(classNode, passes, new CodeGen());
for (IDexTreeVisitor visitor : passes) {
DepthTraversal.visit(visitor, classNode);
}
try { try {
new CodeGen().visit(classNode); ProcessClass.process(classNode, passes, true);
} catch (Exception e) { } catch (Exception e) {
throw new JadxRuntimeException("Codegen failed", e); throw new JadxRuntimeException("Class process failed", e);
} }
LOG.info("----------------------------------------------------------------"); LOG.info("----------------------------------------------------------------");
LOG.info("Print class: {}, {}", classNode.getFullName(), classNode.dex()); LOG.info("Print class: {}, {}", classNode.getFullName(), classNode.dex());
......
...@@ -3,7 +3,6 @@ package jadx.tests.integration.inner; ...@@ -3,7 +3,6 @@ package jadx.tests.integration.inner;
import org.junit.Test; import org.junit.Test;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;
import jadx.tests.api.SmaliTest; import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne; import static jadx.tests.api.utils.JadxMatchers.containsOne;
......
...@@ -3,10 +3,6 @@ package jadx.tests.integration.inner; ...@@ -3,10 +3,6 @@ package jadx.tests.integration.inner;
import org.junit.Test; import org.junit.Test;
import jadx.tests.api.IntegrationTest; 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 { public class TestInnerClassSyntheticConstructor extends IntegrationTest {
......
...@@ -5,7 +5,6 @@ import org.junit.Test; ...@@ -5,7 +5,6 @@ import org.junit.Test;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest; import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
......
...@@ -4,9 +4,7 @@ import jadx.core.dex.nodes.ClassNode; ...@@ -4,9 +4,7 @@ import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest; import jadx.tests.api.IntegrationTest;
import org.junit.Test; import org.junit.Test;
import static jadx.tests.api.utils.JadxMatchers.containsOne;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
......
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"));
}
}
.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;-><init>()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 <init>()V
.registers 1
invoke-direct {p0}, Ljadx/tests/api/SmaliTest;-><init>()V
return-void
.end method
.method private static call(Ljava/lang/String;)V
.registers 1
return-void
.end method
plugins { plugins {
id 'edu.sc.seis.launch4j' version '2.4.4' id 'edu.sc.seis.launch4j' version '2.4.5'
id 'com.github.johnrengelman.shadow' version '4.0.3' id 'com.github.johnrengelman.shadow' version '5.0.0'
} }
apply plugin: 'application' apply plugin: 'application'
...@@ -13,7 +13,8 @@ targetCompatibility = JavaVersion.VERSION_1_8 ...@@ -13,7 +13,8 @@ targetCompatibility = JavaVersion.VERSION_1_8
dependencies { dependencies {
compile(project(":jadx-core")) compile(project(":jadx-core"))
compile(project(":jadx-cli")) 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 'com.google.code.gson:gson:2.8.5'
compile files('libs/jfontchooser-1.0.5.jar') compile files('libs/jfontchooser-1.0.5.jar')
compile 'hu.kazocsaba:image-viewer:1.2.3' compile 'hu.kazocsaba:image-viewer:1.2.3'
...@@ -21,9 +22,9 @@ dependencies { ...@@ -21,9 +22,9 @@ dependencies {
compile 'org.apache.commons:commons-lang3:3.8.1' compile 'org.apache.commons:commons-lang3:3.8.1'
compile 'org.apache.commons:commons-text:1.6' compile 'org.apache.commons:commons-text:1.6'
compile 'io.reactivex.rxjava2:rxjava:2.2.5' compile 'io.reactivex.rxjava2:rxjava:2.2.7'
compile "com.github.akarnokd:rxjava2-swing:0.3.3" compile "com.github.akarnokd:rxjava2-swing:0.3.4"
compile 'com.android.tools.build:apksig:3.3.0' compile 'com.android.tools.build:apksig:3.3.2'
} }
applicationDistribution.with { applicationDistribution.with {
......
package jadx.gui; package jadx.gui;
import javax.swing.*;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.swing.ProgressMonitor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -78,14 +81,13 @@ public class JadxWrapper { ...@@ -78,14 +81,13 @@ public class JadxWrapper {
*/ */
public List<JavaClass> getIncludedClasses() { public List<JavaClass> getIncludedClasses() {
List<JavaClass> classList = decompiler.getClasses(); List<JavaClass> classList = decompiler.getClasses();
String excludedPackages = settings.getExcludedPackages().trim(); List<String> excludedPackages = getExcludedPackages();
if (excludedPackages.length() == 0) { if (excludedPackages.isEmpty()) {
return classList; return classList;
} }
String[] excluded = excludedPackages.split("[ ]+");
return classList.stream().filter(cls -> { return classList.stream().filter(cls -> {
for (String exclude : excluded) { for (String exclude : excludedPackages) {
if (cls.getFullName().startsWith(exclude)) { if (cls.getFullName().startsWith(exclude)) {
return false; return false;
} }
...@@ -94,6 +96,23 @@ public class JadxWrapper { ...@@ -94,6 +96,23 @@ public class JadxWrapper {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
public List<String> 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<String> list = new ArrayList<>(getExcludedPackages());
list.remove(packageToRemoveFromExclusion);
settings.setExcludedPackages(String.join(" ", list));
settings.sync();
}
public List<JavaPackage> getPackages() { public List<JavaPackage> getPackages() {
return decompiler.getPackages(); return decompiler.getPackages();
} }
......
...@@ -333,4 +333,5 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -333,4 +333,5 @@ public class JadxSettings extends JadxCLIArgs {
settingsVersion = CURRENT_SETTINGS_VERSION; settingsVersion = CURRENT_SETTINGS_VERSION;
sync(); sync();
} }
} }
...@@ -247,10 +247,14 @@ public class JadxSettingsWindow extends JDialog { ...@@ -247,10 +247,14 @@ public class JadxSettingsWindow extends JDialog {
JButton editExcludedPackages = new JButton(NLS.str("preferences.excludedPackages.button")); JButton editExcludedPackages = new JButton(NLS.str("preferences.excludedPackages.button"));
editExcludedPackages.addActionListener(event -> { editExcludedPackages.addActionListener(event -> {
String oldExcludedPackages = settings.getExcludedPackages();
String result = JOptionPane.showInputDialog(this, NLS.str("preferences.excludedPackages.editDialog"), String result = JOptionPane.showInputDialog(this, NLS.str("preferences.excludedPackages.editDialog"),
settings.getExcludedPackages()); settings.getExcludedPackages());
if (result != null) { if (result != null) {
settings.setExcludedPackages(result); settings.setExcludedPackages(result);
if (!oldExcludedPackages.equals(result)) {
needReload();
}
} }
}); });
......
package jadx.gui.treemodel; package jadx.gui.treemodel;
import javax.swing.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import jadx.api.JavaClass; import jadx.api.JavaClass;
import jadx.api.JavaPackage; import jadx.api.JavaPackage;
import jadx.gui.JadxWrapper;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
public class JPackage extends JNode implements Comparable<JPackage> { public class JPackage extends JNode implements Comparable<JPackage> {
...@@ -15,12 +18,16 @@ public class JPackage extends JNode implements Comparable<JPackage> { ...@@ -15,12 +18,16 @@ public class JPackage extends JNode implements Comparable<JPackage> {
private static final ImageIcon PACKAGE_ICON = Utils.openIcon("package_obj"); private static final ImageIcon PACKAGE_ICON = Utils.openIcon("package_obj");
private final String fullName;
private String name; private String name;
private boolean enabled;
private final List<JClass> classes; private final List<JClass> classes;
private final List<JPackage> innerPackages = new ArrayList<>(1); private final List<JPackage> innerPackages = new ArrayList<>(1);
public JPackage(JavaPackage pkg) { public JPackage(JavaPackage pkg, JadxWrapper wrapper) {
this.fullName = pkg.getName();
this.name = pkg.getName(); this.name = pkg.getName();
setEnabled(wrapper);
List<JavaClass> javaClasses = pkg.getClasses(); List<JavaClass> javaClasses = pkg.getClasses();
this.classes = new ArrayList<>(javaClasses.size()); this.classes = new ArrayList<>(javaClasses.size());
for (JavaClass javaClass : javaClasses) { for (JavaClass javaClass : javaClasses) {
...@@ -29,13 +36,22 @@ public class JPackage extends JNode implements Comparable<JPackage> { ...@@ -29,13 +36,22 @@ public class JPackage extends JNode implements Comparable<JPackage> {
update(); update();
} }
public JPackage(String name) { public JPackage(String name, JadxWrapper wrapper) {
this.fullName = name;
this.name = name; this.name = name;
setEnabled(wrapper);
this.classes = new ArrayList<>(1); this.classes = new ArrayList<>(1);
} }
private void setEnabled(JadxWrapper wrapper) {
List<String> excludedPackages = wrapper.getExcludedPackages();
this.enabled = excludedPackages.isEmpty()
|| excludedPackages.stream().filter(p -> !p.isEmpty()).noneMatch(p -> name.startsWith(p));
}
public final void update() { public final void update() {
removeAllChildren(); removeAllChildren();
if (isEnabled()) {
for (JPackage pkg : innerPackages) { for (JPackage pkg : innerPackages) {
pkg.update(); pkg.update();
add(pkg); add(pkg);
...@@ -45,12 +61,17 @@ public class JPackage extends JNode implements Comparable<JPackage> { ...@@ -45,12 +61,17 @@ public class JPackage extends JNode implements Comparable<JPackage> {
add(cls); add(cls);
} }
} }
}
@Override @Override
public String getName() { public String getName() {
return name; return name;
} }
public String getFullName() {
return fullName;
}
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
...@@ -108,4 +129,8 @@ public class JPackage extends JNode implements Comparable<JPackage> { ...@@ -108,4 +129,8 @@ public class JPackage extends JNode implements Comparable<JPackage> {
public String makeLongString() { public String makeLongString() {
return name; return name;
} }
public boolean isEnabled() {
return enabled;
}
} }
...@@ -3,6 +3,7 @@ package jadx.gui.treemodel; ...@@ -3,6 +3,7 @@ package jadx.gui.treemodel;
import javax.swing.*; import javax.swing.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -68,6 +69,14 @@ public class JResource extends JLoadableNode implements Comparable<JResource> { ...@@ -68,6 +69,14 @@ public class JResource extends JLoadableNode implements Comparable<JResource> {
} }
} else { } else {
removeAllChildren(); removeAllChildren();
Comparator<JResource> typeComparator
= (r1, r2) -> r1.type.ordinal() - r2.type.ordinal();
Comparator<JResource> nameComparator
= Comparator.comparing(JResource::getName, String.CASE_INSENSITIVE_ORDER);
files.sort(typeComparator.thenComparing(nameComparator));
for (JResource res : files) { for (JResource res : files) {
res.update(); res.update();
add(res); add(res);
......
...@@ -33,7 +33,7 @@ public class JSources extends JNode { ...@@ -33,7 +33,7 @@ public class JSources extends JNode {
removeAllChildren(); removeAllChildren();
if (flatPackages) { if (flatPackages) {
for (JavaPackage pkg : wrapper.getPackages()) { for (JavaPackage pkg : wrapper.getPackages()) {
add(new JPackage(pkg)); add(new JPackage(pkg, wrapper));
} }
} else { } else {
// build packages hierarchy // build packages hierarchy
...@@ -54,7 +54,7 @@ public class JSources extends JNode { ...@@ -54,7 +54,7 @@ public class JSources extends JNode {
List<JPackage> getHierarchyPackages(List<JavaPackage> packages) { List<JPackage> getHierarchyPackages(List<JavaPackage> packages) {
Map<String, JPackage> pkgMap = new HashMap<>(); Map<String, JPackage> pkgMap = new HashMap<>();
for (JavaPackage pkg : packages) { for (JavaPackage pkg : packages) {
addPackage(pkgMap, new JPackage(pkg)); addPackage(pkgMap, new JPackage(pkg, wrapper));
} }
// merge packages without classes // merge packages without classes
boolean repeat; boolean repeat;
...@@ -114,7 +114,7 @@ public class JSources extends JNode { ...@@ -114,7 +114,7 @@ public class JSources extends JNode {
pkg.setName(shortName); pkg.setName(shortName);
JPackage prevPkg = pkgs.get(prevPart); JPackage prevPkg = pkgs.get(prevPart);
if (prevPkg == null) { if (prevPkg == null) {
prevPkg = new JPackage(prevPart); prevPkg = new JPackage(prevPart, wrapper);
addPackage(pkgs, prevPkg); addPackage(pkgs, prevPkg);
} }
prevPkg.getInnerPackages().add(pkg); prevPkg.getInnerPackages().add(pkg);
......
...@@ -2,9 +2,11 @@ package jadx.gui.ui; ...@@ -2,9 +2,11 @@ package jadx.gui.ui;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.net.URL;
import jadx.api.JadxDecompiler; import jadx.api.JadxDecompiler;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Utils;
class AboutDialog extends JDialog { class AboutDialog extends JDialog {
private static final long serialVersionUID = 5763493590584039096L; private static final long serialVersionUID = 5763493590584039096L;
...@@ -16,7 +18,10 @@ class AboutDialog extends JDialog { ...@@ -16,7 +18,10 @@ class AboutDialog extends JDialog {
public final void initUI() { public final void initUI() {
Font font = new Font("Serif", Font.BOLD, 13); 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.setFont(font);
name.setAlignmentX(0.5f); name.setAlignmentX(0.5f);
...@@ -24,10 +29,24 @@ class AboutDialog extends JDialog { ...@@ -24,10 +29,24 @@ class AboutDialog extends JDialog {
desc.setFont(font); desc.setFont(font);
desc.setAlignmentX(0.5f); desc.setAlignmentX(0.5f);
JLabel version = new JLabel("version: " + JadxDecompiler.getVersion()); JLabel version = new JLabel("jadx version: " + JadxDecompiler.getVersion());
version.setFont(font); version.setFont(font);
version.setAlignmentX(0.5f); 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(); JPanel textPane = new JPanel();
textPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); textPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
textPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS)); textPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS));
...@@ -38,6 +57,9 @@ class AboutDialog extends JDialog { ...@@ -38,6 +57,9 @@ class AboutDialog extends JDialog {
textPane.add(Box.createRigidArea(new Dimension(0, 10))); textPane.add(Box.createRigidArea(new Dimension(0, 10)));
textPane.add(version); textPane.add(version);
textPane.add(Box.createRigidArea(new Dimension(0, 20))); 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")); JButton close = new JButton(NLS.str("tabs.close"));
close.addActionListener(event -> dispose()); close.addActionListener(event -> dispose());
...@@ -47,6 +69,8 @@ class AboutDialog extends JDialog { ...@@ -47,6 +69,8 @@ class AboutDialog extends JDialog {
contentPane.add(textPane, BorderLayout.CENTER); contentPane.add(textPane, BorderLayout.CENTER);
contentPane.add(close, BorderLayout.PAGE_END); contentPane.add(close, BorderLayout.PAGE_END);
Utils.setWindowIcons(this);
setModalityType(ModalityType.APPLICATION_MODAL); setModalityType(ModalityType.APPLICATION_MODAL);
setTitle(NLS.str("about_dialog.title")); setTitle(NLS.str("about_dialog.title"));
......
package jadx.gui.ui; package jadx.gui.ui;
import javax.swing.*; import static javax.swing.KeyStroke.getKeyStroke;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener; import java.awt.BorderLayout;
import javax.swing.event.TreeExpansionEvent; import java.awt.Component;
import javax.swing.event.TreeWillExpandListener; import java.awt.DisplayMode;
import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.Font;
import javax.swing.tree.DefaultMutableTreeNode; import java.awt.GraphicsDevice;
import javax.swing.tree.DefaultTreeCellRenderer; import java.awt.GraphicsEnvironment;
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.DnDConstants;
import java.awt.dnd.DropTarget; import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
...@@ -23,12 +18,42 @@ import java.awt.event.MouseAdapter; ...@@ -23,12 +18,42 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; 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;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.fife.ui.rsyntaxtextarea.Theme; import org.fife.ui.rsyntaxtextarea.Theme;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -46,6 +71,7 @@ import jadx.gui.treemodel.JCertificate; ...@@ -46,6 +71,7 @@ import jadx.gui.treemodel.JCertificate;
import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JLoadableNode; import jadx.gui.treemodel.JLoadableNode;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JPackage;
import jadx.gui.treemodel.JResource; import jadx.gui.treemodel.JResource;
import jadx.gui.treemodel.JRoot; import jadx.gui.treemodel.JRoot;
import jadx.gui.update.JadxUpdate; import jadx.gui.update.JadxUpdate;
...@@ -57,8 +83,6 @@ import jadx.gui.utils.Link; ...@@ -57,8 +83,6 @@ import jadx.gui.utils.Link;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
import static javax.swing.KeyStroke.getKeyStroke;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class MainWindow extends JFrame { public class MainWindow extends JFrame {
private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class); private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);
...@@ -82,6 +106,7 @@ public class MainWindow extends JFrame { ...@@ -82,6 +106,7 @@ public class MainWindow extends JFrame {
private static final ImageIcon ICON_PREF = Utils.openIcon("wrench"); private static final ImageIcon ICON_PREF = Utils.openIcon("wrench");
private static final ImageIcon ICON_DEOBF = Utils.openIcon("lock_edit"); private static final ImageIcon ICON_DEOBF = Utils.openIcon("lock_edit");
private static final ImageIcon ICON_LOG = Utils.openIcon("report"); 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 JadxWrapper wrapper;
private final transient JadxSettings settings; private final transient JadxSettings settings;
...@@ -116,16 +141,7 @@ public class MainWindow extends JFrame { ...@@ -116,16 +141,7 @@ public class MainWindow extends JFrame {
registerBundledFonts(); registerBundledFonts();
initUI(); initUI();
initMenuAndToolbar(); initMenuAndToolbar();
setWindowIcons(); Utils.setWindowIcons(this);
}
private void setWindowIcons() {
List<Image> 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);
loadSettings(); loadSettings();
checkForUpdate(); checkForUpdate();
} }
...@@ -323,6 +339,14 @@ public class MainWindow extends JFrame { ...@@ -323,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() { private void syncWithEditor() {
ContentPanel selectedContentPanel = tabbedPane.getSelectedCodePanel(); ContentPanel selectedContentPanel = tabbedPane.getSelectedCodePanel();
if (selectedContentPanel == null) { if (selectedContentPanel == null) {
...@@ -462,7 +486,7 @@ public class MainWindow extends JFrame { ...@@ -462,7 +486,7 @@ public class MainWindow extends JFrame {
logAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_L, logAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); 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 @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
new AboutDialog().setVisible(true); new AboutDialog().setVisible(true);
...@@ -582,8 +606,13 @@ public class MainWindow extends JFrame { ...@@ -582,8 +606,13 @@ public class MainWindow extends JFrame {
tree.addMouseListener(new MouseAdapter() { tree.addMouseListener(new MouseAdapter() {
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
treeRightClickAction(e);
}
else {
treeClickAction(); treeClickAction();
} }
}
}); });
tree.addKeyListener(new KeyAdapter() { tree.addKeyListener(new KeyAdapter() {
@Override @Override
...@@ -602,6 +631,9 @@ public class MainWindow extends JFrame { ...@@ -602,6 +631,9 @@ public class MainWindow extends JFrame {
if (value instanceof JNode) { if (value instanceof JNode) {
setIcon(((JNode) value).getIcon()); setIcon(((JNode) value).getIcon());
} }
if (value instanceof JPackage) {
setEnabled(((JPackage) value).isEnabled());
}
return c; return c;
} }
}); });
...@@ -748,4 +780,23 @@ public class MainWindow extends JFrame { ...@@ -748,4 +780,23 @@ public class MainWindow extends JFrame {
public void menuCanceled(MenuEvent e) { 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();
});
}
}
} }
...@@ -7,6 +7,8 @@ import java.awt.datatransfer.StringSelection; ...@@ -7,6 +7,8 @@ import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; import java.awt.datatransfer.Transferable;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
...@@ -191,4 +193,13 @@ public class Utils { ...@@ -191,4 +193,13 @@ public class Utils {
} }
return sb.toString().trim(); return sb.toString().trim();
} }
public static void setWindowIcons(Window window) {
List<Image> 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);
}
} }
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 252.23 252"><defs><style>.cls-1,.cls-14{fill:none;}.cls-2{fill:#231f20;}.cls-3{fill:#461bbc;}.cls-4{clip-path:url(#clip-path);}.cls-5{clip-path:url(#clip-path-2);}.cls-6{fill:#008275;}.cls-7{font-size:132.44px;}.cls-12,.cls-7{fill:#fff;font-family:FuturaPT-Heavy, Futura PT;}.cls-8{letter-spacing:-0.03em;}.cls-9{clip-path:url(#clip-path-3);}.cls-10{fill:#f15a29;}.cls-11{fill:#9e1f63;}.cls-12{font-size:134.04px;letter-spacing:-0.01em;}.cls-13{letter-spacing:0em;}.cls-14{stroke:#fff;stroke-miterlimit:10;stroke-width:8.91px;}</style><clipPath id="clip-path" transform="translate(0.23)"><circle class="cls-1" cx="126" cy="126" r="125.72"/></clipPath><clipPath id="clip-path-2" transform="translate(0.23)"><polygon class="cls-1" points="45.29 223.57 176.25 75.9 258.09 69.98 258.09 242.47 45.29 242.47 45.29 223.57"/></clipPath><clipPath id="clip-path-3" transform="translate(0.23)"><polygon class="cls-1" points="57.93 195.53 179.7 63.23 69.69 5.58 3.53 19.58 -15.33 132.35 57.93 195.53"/></clipPath></defs><title>jadxlogo</title><circle class="cls-2" cx="126.23" cy="126" r="126"/><path class="cls-3" d="M122.66-68.19" transform="translate(0.23)"/><g class="cls-4"><g class="cls-5"><polygon class="cls-3" points="198.71 223.57 37.48 223.57 142.14 105.88 198.71 223.57"/><polygon class="cls-6" points="170.52 74.83 238.84 87.48 247.15 198.65 190.4 207.09 142.14 105.88 170.52 74.83"/><text class="cls-7" transform="translate(82.68 208.04) scale(0.95 1)"><tspan class="cls-8">D</tspan><tspan x="86.48" y="0">X</tspan></text></g><g class="cls-9"><polyline class="cls-10" points="73.68 193.85 0 126.28 140.52 53.49 171.12 88.7"/><polyline class="cls-11" points="30.86 19.58 30.86 110.86 129.83 59.59 160.42 94.8 181.86 67.19"/><text class="cls-12" transform="translate(10.71 121.84) scale(1.01 1)">J<tspan class="cls-13" x="54.29" y="0">A</tspan></text></g></g><circle class="cls-14" cx="126.08" cy="125.86" r="120.86"/></svg>
\ No newline at end of file
#!/usr/bin/env bash #!/usr/bin/env bash
set -xe set -e
export JFROG_CLI_OFFER_CONFIG=false export JFROG_CLI_OFFER_CONFIG=false
export JFROG_CLI_LOG_LEVEL=DEBUG export JFROG_CLI_LOG_LEVEL=DEBUG
npm install -g jfrog-cli-go npm install -g jfrog-cli-go
TARGET=skylot/jadx/${BINTRAY_PACKAGE}/v${JADX_VERSION} TARGET=${BINTRAY_USER}/jadx/${BINTRAY_PACKAGE}/v${JADX_VERSION}
CREDENTIALS="--user=skylot --key=${BINTRAY_KEY}" CREDENTIALS="--user=${BINTRAY_USER} --key=${BINTRAY_KEY}"
jfrog bt version-create ${TARGET} ${CREDENTIALS} --desc=${JADX_VERSION} jfrog bt version-create ${TARGET} ${CREDENTIALS} --desc=${JADX_VERSION}
jfrog bt upload 'build/jadx.*\.(zip|exe)' ${TARGET} ${CREDENTIALS} --regexp=true --publish=true jfrog bt upload 'build/jadx.*\.(zip|exe)' ${TARGET} ${CREDENTIALS} --regexp=true --publish=true
......
#!/usr/bin/env bash #!/usr/bin/env bash
set -xe set -e
# upload coverage to codecov # upload coverage to codecov
./gradlew clean build jacocoTestReport ./gradlew clean build jacocoTestReport
......
#!/usr/bin/env bash #!/usr/bin/env bash
set -xe set -e
npm install -g semantic-release npm install -g semantic-release
npm install -g semantic-release/exec npm install -g semantic-release/exec
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment