fix: use strict patterns for synthetic methods inline (#1829)

This commit is contained in:
Skylot
2023-04-19 17:05:09 +01:00
parent 50283ab543
commit fdf170529f
4 changed files with 129 additions and 2 deletions
@@ -264,6 +264,13 @@ public abstract class InsnArg extends Typed {
return false;
}
public boolean isSameVar(RegisterArg arg) {
if (isRegister()) {
return ((RegisterArg) this).sameRegAndSVar(arg);
}
return false;
}
protected final <T extends InsnArg> T copyCommonParams(T copy) {
copy.copyAttributesFrom(this);
copy.setParentInsn(parentInsn);
@@ -80,13 +80,47 @@ public class MarkMethodsForInline extends AbstractVisitor {
return addInlineAttr(mth, insn);
}
if (insnsCount == 2 && insns.get(1).getType() == InsnType.RETURN) {
// synthetic field setter
return addInlineAttr(mth, insns.get(0));
InsnNode firstInsn = insns.get(0);
InsnNode retInsn = insns.get(1);
if (retInsn.getArgsCount() == 0
|| isSyntheticAccessPattern(mth, firstInsn, retInsn)) {
return addInlineAttr(mth, firstInsn);
}
}
// TODO: inline field arithmetics. Disabled tests: TestAnonymousClass3a and TestAnonymousClass5
return null;
}
private static boolean isSyntheticAccessPattern(MethodNode mth, InsnNode firstInsn, InsnNode retInsn) {
List<RegisterArg> mthRegs = mth.getArgRegs();
switch (firstInsn.getType()) {
case IGET:
return mthRegs.size() == 1
&& retInsn.getArg(0).isSameVar(firstInsn.getResult())
&& firstInsn.getArg(0).isSameVar(mthRegs.get(0));
case SGET:
return mthRegs.size() == 0
&& retInsn.getArg(0).isSameVar(firstInsn.getResult());
case IPUT:
return mthRegs.size() == 2
&& retInsn.getArg(0).isSameVar(mthRegs.get(1))
&& firstInsn.getArg(0).isSameVar(mthRegs.get(1))
&& firstInsn.getArg(1).isSameVar(mthRegs.get(0));
case SPUT:
return mthRegs.size() == 1
&& retInsn.getArg(0).isSameVar(mthRegs.get(0))
&& firstInsn.getArg(0).isSameVar(mthRegs.get(0));
case INVOKE:
return mthRegs.size() >= 1
&& firstInsn.getArg(0).isSameVar(mthRegs.get(0))
&& retInsn.getArg(0).isSameVar(firstInsn.getResult());
default:
return false;
}
}
private static MethodInlineAttr addInlineAttr(MethodNode mth, InsnNode insn) {
if (!fixVisibilityOfInlineCode(mth, insn)) {
return null;
@@ -9,6 +9,8 @@ import java.util.Set;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.InvokeCustomNode;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.InsnWrapArg;
import jadx.core.dex.instructions.args.Named;
@@ -123,6 +125,9 @@ public class CodeShrinkVisitor extends AbstractVisitor {
return;
}
}
if (!checkLambdaInline(arg, assignInsn)) {
return;
}
int assignPos = insnList.getIndex(assignInsn);
if (assignPos != -1) {
@@ -145,6 +150,26 @@ public class CodeShrinkVisitor extends AbstractVisitor {
}
}
/**
* Forbid inline lambda into invoke as an instance arg, i.e. this will not compile:
* {@code () -> { ... }.apply(); }
*/
private static boolean checkLambdaInline(RegisterArg arg, InsnNode assignInsn) {
if (assignInsn.getType() == InsnType.INVOKE && assignInsn instanceof InvokeCustomNode) {
for (RegisterArg useArg : arg.getSVar().getUseList()) {
InsnNode parentInsn = useArg.getParentInsn();
if (parentInsn != null && parentInsn.getType() == InsnType.INVOKE) {
InvokeNode invokeNode = (InvokeNode) parentInsn;
InsnArg instArg = invokeNode.getInstanceArg();
if (instArg != null && instArg == useArg) {
return false;
}
}
}
}
return true;
}
private static boolean varWithSameNameExists(MethodNode mth, SSAVar inlineVar) {
for (SSAVar ssaVar : mth.getSVars()) {
if (ssaVar == inlineVar || ssaVar.getCodeVar() == inlineVar.getCodeVar()) {
@@ -0,0 +1,61 @@
package jadx.tests.integration.java8;
import org.junit.jupiter.api.Test;
import jadx.tests.api.IntegrationTest;
import jadx.tests.api.extensions.profiles.TestProfile;
import jadx.tests.api.extensions.profiles.TestWithProfiles;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestLambdaReturn extends IntegrationTest {
@SuppressWarnings("unused")
public static class TestCls {
interface Function0<R> {
R apply();
}
public static class T2 {
public long l;
public T2(long l) {
this.l = l;
}
public void w() {
}
}
public Byte test(Byte b1) {
Function0<Void> f1 = () -> {
new T2(94L).w();
return null;
};
f1.apply();
return null;
}
}
@TestWithProfiles(TestProfile.DX_J8)
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsLines(2,
"Function0<Void> f1 = () -> {",
indent() + "new T2(94L).w();",
indent() + "return null;",
"};");
}
@TestWithProfiles(TestProfile.D8_J11_DESUGAR)
public void testLambda() {
getClassNode(TestCls.class);
}
@Test
public void testNoDebug() {
noDebugInfo();
getClassNode(TestCls.class);
}
}