core: inline anonymous classes
This commit is contained in:
@@ -20,4 +20,5 @@ public class Consts {
|
||||
public static final String DALVIK_ANNOTATION_DEFAULT = "dalvik.annotation.AnnotationDefault";
|
||||
|
||||
public static final String DEFAULT_PACKAGE_NAME = "defpackage";
|
||||
public static final String ANONYMOUS_CLASS_PREFIX = "AnonymousClass_";
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ public class ClassGen {
|
||||
mthGen.makeMethodDump(code);
|
||||
}
|
||||
mthGen.addDefinition(code);
|
||||
code.add(" {");
|
||||
code.add('{');
|
||||
insertSourceFileInfo(code, mth);
|
||||
code.add(mthGen.makeInstructions(code.getIndent()));
|
||||
code.startLine('}');
|
||||
|
||||
@@ -33,6 +33,7 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
import jadx.core.utils.RegionUtils;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
@@ -523,13 +524,18 @@ public class InsnGen {
|
||||
if (cls != null && cls.isAnonymous()) {
|
||||
// anonymous class construction
|
||||
ClassInfo parent;
|
||||
if (cls.getSuperClass() != null
|
||||
&& !cls.getSuperClass().getFullName().equals("java.lang.Object"))
|
||||
if (cls.getSuperClass() != null && !cls.getSuperClass().isObject()) {
|
||||
parent = cls.getSuperClass();
|
||||
else
|
||||
} else {
|
||||
parent = cls.getInterfaces().get(0);
|
||||
|
||||
code.add("new ").add(useClass(parent)).add("()");
|
||||
}
|
||||
MethodNode defCtr = cls.getDefaultConstructor();
|
||||
if (RegionUtils.notEmpty(defCtr.getRegion())) {
|
||||
defCtr.getAttributes().add(AttributeFlag.ANONYMOUS_CONSTRUCTOR);
|
||||
} else {
|
||||
defCtr.getAttributes().add(AttributeFlag.DONT_GENERATE);
|
||||
}
|
||||
code.add("new ").add(useClass(parent)).add("() ");
|
||||
code.incIndent(2);
|
||||
new ClassGen(cls, mgen.getClassGen().getParentGen(), fallback).makeClassBody(code);
|
||||
code.decIndent(2);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.attributes.AttributeFlag;
|
||||
import jadx.core.dex.attributes.AttributeType;
|
||||
import jadx.core.dex.attributes.AttributesList;
|
||||
import jadx.core.dex.attributes.JadxErrorAttr;
|
||||
@@ -57,52 +58,61 @@ public class MethodGen {
|
||||
}
|
||||
|
||||
public void addDefinition(CodeWriter code) {
|
||||
|
||||
if (mth.getMethodInfo().isClassInit()) {
|
||||
code.startLine("static");
|
||||
} else {
|
||||
annotationGen.addForMethod(code, mth);
|
||||
|
||||
AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags();
|
||||
AccessInfo ai = mth.getAccessFlags();
|
||||
// don't add 'abstract' to methods in interface
|
||||
if (clsAccFlags.isInterface()) {
|
||||
ai = ai.remove(AccessFlags.ACC_ABSTRACT);
|
||||
}
|
||||
// don't add 'public' for annotations
|
||||
if (clsAccFlags.isAnnotation()) {
|
||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||
}
|
||||
code.startLine(ai.makeString());
|
||||
|
||||
if (classGen.makeGenericMap(code, mth.getGenericMap()))
|
||||
code.add(' ');
|
||||
|
||||
if (mth.getAccessFlags().isConstructor()) {
|
||||
code.add(classGen.getClassNode().getShortName()); // constructor
|
||||
} else {
|
||||
code.add(TypeGen.translate(classGen, mth.getReturnType()));
|
||||
code.add(' ');
|
||||
code.add(mth.getName());
|
||||
}
|
||||
code.add('(');
|
||||
|
||||
List<RegisterArg> args = mth.getArguments(false);
|
||||
if (mth.getMethodInfo().isConstructor()
|
||||
&& mth.getParentClass().getAttributes().contains(AttributeType.ENUM_CLASS)) {
|
||||
if (args.size() == 2)
|
||||
args.clear();
|
||||
else if (args.size() > 2)
|
||||
args = args.subList(2, args.size());
|
||||
else
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth,
|
||||
"Incorrect number of args for enum constructor: " + args.size()
|
||||
+ " (expected >= 2)"));
|
||||
}
|
||||
code.add(makeArguments(args));
|
||||
code.add(")");
|
||||
|
||||
annotationGen.addThrows(mth, code);
|
||||
code.attachAnnotation(mth);
|
||||
return;
|
||||
}
|
||||
if (mth.getAttributes().contains(AttributeFlag.ANONYMOUS_CONSTRUCTOR)) {
|
||||
// don't add method name and arguments
|
||||
code.startLine();
|
||||
code.attachAnnotation(mth);
|
||||
return;
|
||||
}
|
||||
annotationGen.addForMethod(code, mth);
|
||||
|
||||
AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags();
|
||||
AccessInfo ai = mth.getAccessFlags();
|
||||
// don't add 'abstract' to methods in interface
|
||||
if (clsAccFlags.isInterface()) {
|
||||
ai = ai.remove(AccessFlags.ACC_ABSTRACT);
|
||||
}
|
||||
// don't add 'public' for annotations
|
||||
if (clsAccFlags.isAnnotation()) {
|
||||
ai = ai.remove(AccessFlags.ACC_PUBLIC);
|
||||
}
|
||||
code.startLine(ai.makeString());
|
||||
|
||||
if (classGen.makeGenericMap(code, mth.getGenericMap())) {
|
||||
code.add(' ');
|
||||
}
|
||||
if (mth.getAccessFlags().isConstructor()) {
|
||||
code.add(classGen.getClassNode().getShortName()); // constructor
|
||||
} else {
|
||||
code.add(TypeGen.translate(classGen, mth.getReturnType()));
|
||||
code.add(' ');
|
||||
code.add(mth.getName());
|
||||
}
|
||||
code.add('(');
|
||||
|
||||
List<RegisterArg> args = mth.getArguments(false);
|
||||
if (mth.getMethodInfo().isConstructor()
|
||||
&& mth.getParentClass().getAttributes().contains(AttributeType.ENUM_CLASS)) {
|
||||
if (args.size() == 2) {
|
||||
args.clear();
|
||||
} else if (args.size() > 2) {
|
||||
args = args.subList(2, args.size());
|
||||
} else {
|
||||
LOG.warn(ErrorsCounter.formatErrorMsg(mth,
|
||||
"Incorrect number of args for enum constructor: " + args.size()
|
||||
+ " (expected >= 2)"));
|
||||
}
|
||||
}
|
||||
code.add(makeArguments(args));
|
||||
code.add(") ");
|
||||
|
||||
annotationGen.addThrows(mth, code);
|
||||
code.attachAnnotation(mth);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ public enum AttributeFlag {
|
||||
SKIP,
|
||||
|
||||
SKIP_FIRST_ARG,
|
||||
ANONYMOUS_CONSTRUCTOR,
|
||||
|
||||
INCONSISTENT_CODE, // warning about incorrect decompilation
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public final class ClassInfo {
|
||||
|
||||
char firstChar = name.charAt(0);
|
||||
if (Character.isDigit(firstChar)) {
|
||||
name = "AnonymousClass_" + name;
|
||||
name = Consts.ANONYMOUS_CLASS_PREFIX + name;
|
||||
} else if (firstChar == '$') {
|
||||
name = "_" + name;
|
||||
}
|
||||
@@ -101,6 +101,10 @@ public final class ClassInfo {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public boolean isObject() {
|
||||
return fullName.equals(Consts.CLASS_OBJECT);
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -338,15 +338,19 @@ public class ClassNode extends LineAttrNode implements ILoadable {
|
||||
}
|
||||
|
||||
public boolean isAnonymous() {
|
||||
boolean simple = false;
|
||||
for (MethodNode m : methods) {
|
||||
MethodInfo mi = m.getMethodInfo();
|
||||
if (mi.isConstructor() && mi.getArgumentsTypes().size() == 0) {
|
||||
simple = true;
|
||||
break;
|
||||
MethodNode defConstrExists = getDefaultConstructor();
|
||||
return defConstrExists != null && getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX);
|
||||
}
|
||||
|
||||
public MethodNode getDefaultConstructor() {
|
||||
for (MethodNode mth : methods) {
|
||||
if (mth.getAccessFlags().isConstructor()
|
||||
&& mth.getMethodInfo().isConstructor()
|
||||
&& mth.getArguments(false).isEmpty()) {
|
||||
return mth;
|
||||
}
|
||||
}
|
||||
return simple && Character.isDigit(getShortName().charAt(0));
|
||||
return null;
|
||||
}
|
||||
|
||||
public AccessInfo getAccessFlags() {
|
||||
|
||||
@@ -140,6 +140,8 @@ public abstract class InternalJadxTest {
|
||||
}
|
||||
}
|
||||
|
||||
// Use only for debug purpose
|
||||
@Deprecated
|
||||
protected void setOutputCFG() {
|
||||
this.outputCFG = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package jadx.tests.internal;
|
||||
|
||||
import jadx.api.InternalJadxTest;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestAnonymousClass extends InternalJadxTest {
|
||||
|
||||
public static class TestCls {
|
||||
|
||||
public int test() {
|
||||
String[] files = new File("a").list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.equals("a");
|
||||
}
|
||||
});
|
||||
return files.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsString("new File(\"a\").list(new FilenameFilter()"));
|
||||
assertThat(code, not(containsString("synthetic")));
|
||||
assertThat(code, not(containsString("this")));
|
||||
assertThat(code, not(containsString("null")));
|
||||
assertThat(code, not(containsString("AnonymousClass_")));
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,6 @@ public class TestLoopCondition extends InternalJadxTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
setOutputCFG();
|
||||
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
|
||||
@@ -51,6 +51,18 @@ public class TestInner extends AbstractTest {
|
||||
}.run();
|
||||
}
|
||||
|
||||
public void func2() {
|
||||
new Runnable() {
|
||||
{
|
||||
count += 5;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
count += 6;
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class MyException extends Exception {
|
||||
public MyException(String str, Exception e) {
|
||||
@@ -63,6 +75,7 @@ public class TestInner extends AbstractTest {
|
||||
TestInner c = new TestInner();
|
||||
TestInner.count = 0;
|
||||
c.func();
|
||||
c.func2();
|
||||
|
||||
Runnable myRunnable = new Runnable() {
|
||||
@Override
|
||||
@@ -81,6 +94,6 @@ public class TestInner extends AbstractTest {
|
||||
thread.join();
|
||||
thread2.join();
|
||||
|
||||
return TestInner.count == 15;
|
||||
return TestInner.count == 26;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user