Skip to content

Commit

Permalink
fix: use strict patterns for synthetic methods inline (#1829)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Apr 19, 2023
1 parent 50283ab commit fdf1705
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ public boolean isSameConst(InsnArg other) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,47 @@ private static MethodInlineAttr inlineMth(MethodNode mth) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

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;
Expand Down Expand Up @@ -123,6 +125,9 @@ private static void checkInline(MethodNode mth, BlockNode block, InsnList insnLi
return;
}
}
if (!checkLambdaInline(arg, assignInsn)) {
return;
}

int assignPos = insnList.getIndex(assignInsn);
if (assignPos != -1) {
Expand All @@ -145,6 +150,26 @@ && canMoveBetweenBlocks(mth, assignInsn, assignBlock, block, argsInfo.getInsn())
}
}

/**
* 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()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit fdf1705

Please sign in to comment.