fix: handle anonymous class self inlining (#604)
This commit is contained in:
@@ -36,6 +36,7 @@ import jadx.core.utils.CodeGenUtils;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class ClassGen {
|
||||
|
||||
@@ -261,6 +262,9 @@ public class ClassGen {
|
||||
try {
|
||||
addMethod(code, mth);
|
||||
} catch (Exception e) {
|
||||
if (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {
|
||||
throw new JadxRuntimeException("Method generation error", e);
|
||||
}
|
||||
code.newLine().add("/*");
|
||||
code.newLine().addMultiLine(ErrorsCounter.methodError(mth, "Method generation error", e));
|
||||
Utils.appendStackTrace(code, e);
|
||||
|
||||
@@ -3,6 +3,7 @@ package jadx.core.codegen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class CodeGen {
|
||||
|
||||
@@ -11,7 +12,18 @@ public class CodeGen {
|
||||
cls.setCode(CodeWriter.EMPTY);
|
||||
} else {
|
||||
ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
|
||||
cls.setCode(clsGen.makeClass());
|
||||
CodeWriter code;
|
||||
try {
|
||||
code = clsGen.makeClass();
|
||||
} catch (Exception e) {
|
||||
if (cls.contains(AFlag.RESTART_CODEGEN)) {
|
||||
cls.remove(AFlag.RESTART_CODEGEN);
|
||||
code = clsGen.makeClass();
|
||||
} else {
|
||||
throw new JadxRuntimeException("Code generation error", e);
|
||||
}
|
||||
}
|
||||
cls.setCode(code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -591,6 +591,14 @@ public class InsnGen {
|
||||
}
|
||||
|
||||
private void inlineAnonymousConstructor(CodeWriter code, ClassNode cls, ConstructorInsn insn) throws CodegenException {
|
||||
if (this.mth.getParentClass() == cls) {
|
||||
cls.remove(AFlag.ANONYMOUS_CLASS);
|
||||
cls.remove(AFlag.DONT_GENERATE);
|
||||
mth.getParentClass().getTopParentClass().add(AFlag.RESTART_CODEGEN);
|
||||
throw new CodegenException("Anonymous inner class unlimited recursion detected."
|
||||
+ " Convert class to inner: " + cls.getClassInfo().getFullName());
|
||||
}
|
||||
|
||||
cls.add(AFlag.DONT_GENERATE);
|
||||
ArgType parent;
|
||||
if (cls.getInterfaces().size() == 1) {
|
||||
|
||||
@@ -206,6 +206,9 @@ public class MethodGen {
|
||||
classGen.insertDecompilationProblems(code, mth);
|
||||
addInstructions(code);
|
||||
} catch (Exception e) {
|
||||
if (mth.getParentClass().getTopParentClass().contains(AFlag.RESTART_CODEGEN)) {
|
||||
throw e;
|
||||
}
|
||||
mth.addError("Method code generation error", e);
|
||||
classGen.insertDecompilationProblems(code, mth);
|
||||
addInstructions(code);
|
||||
|
||||
@@ -15,6 +15,7 @@ public enum AFlag {
|
||||
DONT_WRAP,
|
||||
DONT_INLINE,
|
||||
DONT_GENERATE, // process as usual, but don't output to generated code
|
||||
RESTART_CODEGEN,
|
||||
DONT_RENAME, // do not rename during deobfuscation
|
||||
REMOVE, // can be completely removed
|
||||
ADDED_TO_REGION,
|
||||
|
||||
@@ -183,6 +183,9 @@ public class InputFile {
|
||||
if (pathList.isEmpty()) {
|
||||
throw new JadxException("Empty dx output");
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("result dex files: {}", pathList);
|
||||
}
|
||||
return pathList;
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
|
||||
|
||||
@@ -41,7 +41,7 @@ public class JavaToDex {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream errOut = new ByteArrayOutputStream()) {
|
||||
DxContext context = new DxContext(out, errOut);
|
||||
Path dir = FileUtils.createTempDir(jar.getFileName().toString());
|
||||
Path dir = FileUtils.createTempDir("jar-to-dex-");
|
||||
DxArgs args = new DxArgs(
|
||||
context,
|
||||
dir.toAbsolutePath().toString(),
|
||||
@@ -58,7 +58,6 @@ public class JavaToDex {
|
||||
child.toFile().deleteOnExit();
|
||||
}
|
||||
}
|
||||
dir.toFile().deleteOnExit();
|
||||
return list;
|
||||
} catch (Exception e) {
|
||||
throw new JadxException("dx exception: " + e.getMessage(), e);
|
||||
|
||||
@@ -49,6 +49,10 @@ public abstract class SmaliTest extends IntegrationTest {
|
||||
return getClassNodeFromFile(outDex, pkg + '.' + clsName);
|
||||
}
|
||||
|
||||
protected ClassNode getClassNodeFromSmaliFiles(String clsName) {
|
||||
return searchCls(loadFromSmaliFiles(), getTestPkg() + '.' + clsName);
|
||||
}
|
||||
|
||||
protected List<ClassNode> loadFromSmaliFiles() {
|
||||
File outDex = createTempFile(".dex");
|
||||
compileSmali(outDex, collectSmaliFiles(getTestPkg(), getTestName()));
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package jadx.tests.integration.inner;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.SmaliTest;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static jadx.tests.api.utils.JadxMatchers.countString;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
public class TestIncorrectAnonymousClass extends SmaliTest {
|
||||
|
||||
// @formatter:off
|
||||
/*
|
||||
public static class TestCls {
|
||||
public final class 1 {
|
||||
public void invoke() {
|
||||
new 1(); // cause infinite self inline
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
new 1();
|
||||
}
|
||||
}
|
||||
*/
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNodeFromSmaliFiles("TestCls");
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("public final class AnonymousClass1 {"));
|
||||
assertThat(code, countString(2, "new AnonymousClass1();"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
.class public final Linner/TestCls$1;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 1
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public invoke()V
|
||||
.registers 2
|
||||
|
||||
new-instance v0, Linner/TestCls$1;
|
||||
|
||||
invoke-direct {v0}, Linner/TestCls$1;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,21 @@
|
||||
.class public Linner/TestCls;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 1
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public test()V
|
||||
.registers 2
|
||||
|
||||
new-instance v0, Linner/TestCls$1;
|
||||
|
||||
invoke-direct {v0}, Linner/TestCls$1;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
Reference in New Issue
Block a user