提交 1c60e5e3 编写于 作者: S Skylot

core: inline anonymous classes

上级 a9290f31
......@@ -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);
......
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());
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);
if (classGen.makeGenericMap(code, mth.getGenericMap()))
code.add(' ');
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 (mth.getAccessFlags().isConstructor()) {
code.add(classGen.getClassNode().getShortName()); // constructor
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 {
code.add(TypeGen.translate(classGen, mth.getReturnType()));
code.add(' ');
code.add(mth.getName());
LOG.warn(ErrorsCounter.formatErrorMsg(mth,
"Incorrect number of args for enum constructor: " + args.size()
+ " (expected >= 2)"));
}
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.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;
}
......
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;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册