fix: invoke in nested anonymous classes (#1305)

This commit is contained in:
Skylot
2021-12-12 21:12:05 +00:00
parent 9c446ebbd6
commit d2bde0be21
5 changed files with 104 additions and 3 deletions
@@ -763,8 +763,7 @@ public class InsnGen {
case VIRTUAL:
case INTERFACE:
InsnArg arg = insn.getArg(0);
// FIXME: add 'this' for equals methods in scope
if (!arg.isThis()) {
if (needInvokeArg(arg)) {
addArgDot(code, arg);
}
k++;
@@ -799,6 +798,20 @@ public class InsnGen {
generateMethodArguments(code, insn, k, callMthNode);
}
// FIXME: add 'this' for equals methods in scope
private boolean needInvokeArg(InsnArg arg) {
if (arg.isAnyThis()) {
if (arg.isThis()) {
return false;
}
ClassNode clsNode = mth.root().resolveClass(arg.getType());
if (clsNode != null && clsNode.contains(AFlag.DONT_GENERATE)) {
return false;
}
}
return true;
}
private void makeInvokeLambda(ICodeWriter code, InvokeCustomNode customNode) throws CodegenException {
if (customNode.isUseRef()) {
makeRefLambda(code, customNode);
@@ -232,6 +232,27 @@ public abstract class InsnArg extends Typed {
return contains(AFlag.THIS);
}
/**
* Return true for 'this' from other classes (often occur in anonymous classes)
*/
public boolean isAnyThis() {
if (contains(AFlag.THIS)) {
return true;
}
InsnNode wrappedInsn = unwrap();
if (wrappedInsn != null && wrappedInsn.getType() == InsnType.IGET) {
return wrappedInsn.getArg(0).isAnyThis();
}
return false;
}
public InsnNode unwrap() {
if (isInsnWrap()) {
return ((InsnWrapArg) this).getWrapInsn();
}
return null;
}
public boolean isConst() {
return isLiteral() || (isInsnWrap() && ((InsnWrapArg) this).getWrapInsn().isConstInsn());
}
@@ -97,7 +97,7 @@ public class RenameVisitor extends AbstractVisitor {
// check inner classes names
ClassInfo parentClass = classInfo.getParentClass();
while (parentClass != null) {
if (parentClass.getAliasShortName().equals(clsName)) {
if (parentClass.getAliasShortName().equals(newShortName)) {
String clsAlias = deobfuscator.getClsAlias(cls);
classInfo.changeShortName(clsAlias);
cls.addAttr(new RenameReasonAttr(cls).append("collision with other inner class name"));
@@ -74,6 +74,13 @@ public class JadxCodeAssertions extends AbstractStringAssert<JadxCodeAssertions>
return newCode;
}
public JadxCodeAssertions removeLineComments() {
String code = actual.replaceAll("//.*(?!$)", "");
JadxCodeAssertions newCode = new JadxCodeAssertions(code);
newCode.print();
return newCode;
}
public JadxCodeAssertions print() {
System.out.println("-----------------------------------------------------------");
System.out.println(actual);
@@ -0,0 +1,60 @@
package jadx.tests.integration.inner;
import org.junit.jupiter.api.Test;
import jadx.api.CommentsLevel;
import jadx.tests.api.IntegrationTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestAnonymousClass18 extends IntegrationTest {
@SuppressWarnings({ "Convert2Lambda", "Anonymous2MethodRef", "unused" })
public static class TestCls {
public interface Job {
void executeJob();
}
public void start() {
runJob(new Job() {
@Override
public void executeJob() {
runJob(new Job() {
@Override
public void executeJob() {
doSomething();
}
});
}
private void doSomething() {
}
});
}
public static void runJob(Job job) {
}
}
@Test
public void test() {
getArgs().setCommentsLevel(CommentsLevel.WARN);
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("AnonymousClass1.this")
.doesNotContain("class AnonymousClass1")
// .doesNotContain("TestAnonymousClass18$TestCls.runJob(") // TODO: ???
.containsOne(indent() + "doSomething();");
}
@Test
public void testNoInline() {
getArgs().setInlineAnonymousClasses(false);
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("class AnonymousClass1 implements Job {")
.containsOne("class C00001 implements Job {")
.containsOne("AnonymousClass1.this.doSomething();");
}
}