From 60fb458024f38f7adb3fc29295011626a6230a2d Mon Sep 17 00:00:00 2001 From: pubiqq <82187521+pubiqq@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:27:08 +0300 Subject: [PATCH] fix: improve inlining synthetic accessors (PR #2243) * fix: fix inlining synthetic accessors * add test, undo changes in InsnNode --------- Co-authored-by: Skylot <118523+skylot@users.noreply.github.com> --- .../java/jadx/core/dex/nodes/InsnNode.java | 4 ++ .../dex/visitors/MarkMethodsForInline.java | 34 +++++++++------ .../main/java/jadx/core/utils/ListUtils.java | 21 ++++++++++ .../inline/TestSyntheticClassInline.java | 23 ++++++++++ .../inline/TestSyntheticClassInline/A.smali | 42 +++++++++++++++++++ .../inline/TestSyntheticClassInline/B.smali | 25 +++++++++++ 6 files changed, 136 insertions(+), 13 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticClassInline.java create mode 100644 jadx-core/src/test/smali/inline/TestSyntheticClassInline/A.smali create mode 100644 jadx-core/src/test/smali/inline/TestSyntheticClassInline/B.smali diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java index ba5a49fc8..086e8abd5 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/InsnNode.java @@ -95,6 +95,10 @@ public class InsnNode extends LineAttrNode { return arguments; } + public List getArgList() { + return arguments; + } + public int getArgsCount() { return arguments.size(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/MarkMethodsForInline.java b/jadx-core/src/main/java/jadx/core/dex/visitors/MarkMethodsForInline.java index be856c50d..511ad7ce1 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/MarkMethodsForInline.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/MarkMethodsForInline.java @@ -20,6 +20,7 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.BlockUtils; +import jadx.core.utils.ListUtils; import jadx.core.utils.exceptions.JadxException; @JadxVisitor( @@ -42,18 +43,22 @@ public class MarkMethodsForInline extends AbstractVisitor { */ @Nullable public static MethodInlineAttr process(MethodNode mth) { - MethodInlineAttr mia = mth.get(AType.METHOD_INLINE); - if (mia != null) { - return mia; - } - if (mth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE)) { - if (mth.getBasicBlocks() == null) { - return null; + try { + MethodInlineAttr mia = mth.get(AType.METHOD_INLINE); + if (mia != null) { + return mia; } - MethodInlineAttr inlined = inlineMth(mth); - if (inlined != null) { - return inlined; + if (mth.contains(AFlag.METHOD_CANDIDATE_FOR_INLINE)) { + if (mth.getBasicBlocks() == null) { + return null; + } + MethodInlineAttr inlined = inlineMth(mth); + if (inlined != null) { + return inlined; + } } + } catch (Exception e) { + mth.addWarnComment("Method inline analysis failed", e); } return MethodInlineAttr.inlineNotNeeded(mth); } @@ -113,9 +118,12 @@ public class MarkMethodsForInline extends AbstractVisitor { && firstInsn.getArg(0).isSameVar(mthRegs.get(0)); case INVOKE: - return !mthRegs.isEmpty() - && firstInsn.getArg(0).isSameVar(mthRegs.get(0)) - && retInsn.getArg(0).isSameVar(firstInsn.getResult()); + if (!retInsn.getArg(0).isSameVar(firstInsn.getResult())) { + return false; + } + return ListUtils.orderedEquals( + mth.getArgRegs(), firstInsn.getArgList(), + (mthArg, insnArg) -> insnArg.isSameVar(mthArg)); default: return false; } diff --git a/jadx-core/src/main/java/jadx/core/utils/ListUtils.java b/jadx-core/src/main/java/jadx/core/utils/ListUtils.java index 44a0546ea..928a169e9 100644 --- a/jadx-core/src/main/java/jadx/core/utils/ListUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/ListUtils.java @@ -4,11 +4,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.TreeSet; +import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.Predicate; @@ -30,6 +32,25 @@ public class ListUtils { return first.containsAll(second); } + public static boolean orderedEquals(List list1, List list2, BiPredicate comparer) { + if (list1 == list2) { + return true; + } + if (list1.size() != list2.size()) { + return false; + } + final Iterator iter1 = list1.iterator(); + final Iterator iter2 = list2.iterator(); + while (iter1.hasNext() && iter2.hasNext()) { + final T item1 = iter1.next(); + final U item2 = iter2.next(); + if (!comparer.test(item1, item2)) { + return false; + } + } + return !iter1.hasNext() && !iter2.hasNext(); + } + public static List map(Collection list, Function mapFunc) { if (list == null || list.isEmpty()) { return Collections.emptyList(); diff --git a/jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticClassInline.java b/jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticClassInline.java new file mode 100644 index 000000000..85acf7d1c --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/inline/TestSyntheticClassInline.java @@ -0,0 +1,23 @@ +package jadx.tests.integration.inline; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.SmaliTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestSyntheticClassInline extends SmaliTest { + + @Test + public void test() { + List classes = loadFromSmaliFiles(); + assertThat(searchCls(classes, "inline.A")) + .code() + .containsOne("static Supplier test(final long x1, final long x2) {") + .containsOne("return new Supplier() {") + .containsOne("return A.lambda$test$0(x1, x2);"); + } +} diff --git a/jadx-core/src/test/smali/inline/TestSyntheticClassInline/A.smali b/jadx-core/src/test/smali/inline/TestSyntheticClassInline/A.smali new file mode 100644 index 000000000..0238887da --- /dev/null +++ b/jadx-core/src/test/smali/inline/TestSyntheticClassInline/A.smali @@ -0,0 +1,42 @@ +.class Linline/A; +.super Ljava/lang/Object; +.source "TestJavaClass.java" + +.method constructor ()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;->()V + return-void +.end method + +.method static synthetic lambda$test$0(JJ)Ljava/lang/Long; + .registers 8 + .param p0, "x1" # J + .param p2, "x2" # J + + invoke-static {}, Ljava/lang/System;->currentTimeMillis()J + move-result-wide v0 + .local v0, "y1":J + add-long v2, p0, v0 + add-long/2addr v2, p2 + invoke-static {v2, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long; + move-result-object v2 + return-object v2 +.end method + +.method static test(JJ)Ljava/util/function/Supplier; + .registers 5 + .param p0, "x1" # J + .param p2, "x2" # J + .annotation system Ldalvik/annotation/Signature; + value = { + "(JJ)", + "Ljava/util/function/Supplier<", + "Ljava/lang/Long;", + ">;" + } + .end annotation + + new-instance v0, Linline/A$$ExternalSyntheticLambda0; + invoke-direct {v0, p0, p1, p2, p3}, Linline/A$$ExternalSyntheticLambda0;->(JJ)V + return-object v0 +.end method diff --git a/jadx-core/src/test/smali/inline/TestSyntheticClassInline/B.smali b/jadx-core/src/test/smali/inline/TestSyntheticClassInline/B.smali new file mode 100644 index 000000000..65f779f15 --- /dev/null +++ b/jadx-core/src/test/smali/inline/TestSyntheticClassInline/B.smali @@ -0,0 +1,25 @@ +.class public final synthetic Linline/A$$ExternalSyntheticLambda0; +.super Ljava/lang/Object; +.source "D8$$SyntheticClass" + +.implements Ljava/util/function/Supplier; + +.field public final synthetic f$0:J +.field public final synthetic f$1:J + +.method public synthetic constructor (JJ)V + .registers 5 + invoke-direct {p0}, Ljava/lang/Object;->()V + iput-wide p1, p0, Linline/A$$ExternalSyntheticLambda0;->f$0:J + iput-wide p3, p0, Linline/A$$ExternalSyntheticLambda0;->f$1:J + return-void +.end method + +.method public final get()Ljava/lang/Object; + .registers 5 + iget-wide v0, p0, Linline/A$$ExternalSyntheticLambda0;->f$0:J + iget-wide v2, p0, Linline/A$$ExternalSyntheticLambda0;->f$1:J + invoke-static {v0, v1, v2, v3}, Linline/A;->lambda$test$0(JJ)Ljava/lang/Long; + move-result-object v0 + return-object v0 +.end method