fix: use strict patterns for synthetic methods inline (#1829)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user