From 0c1f830f942e0abdb2701eb262c2ef001e846e64 Mon Sep 17 00:00:00 2001 From: Skylot Date: Wed, 3 Apr 2024 20:10:00 +0100 Subject: [PATCH] fix: lambda decoding and code generation (#2139) --- .../main/java/jadx/core/codegen/InsnGen.java | 4 +- .../java8/TestLambdaInstance3.java | 54 +++++++++++++++++++ .../raung/java8/TestLambdaInstance3.raung | 45 ++++++++++++++++ .../input/java/data/ConstPoolReader.java | 2 + 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance3.java create mode 100644 jadx-core/src/test/raung/java8/TestLambdaInstance3.raung diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index a2aba3ffa..6f33d37f3 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -936,7 +936,7 @@ public class InsnGen { makeInlinedLambdaMethod(code, customNode, callMth); } - private void makeRefLambda(ICodeWriter code, InvokeCustomNode customNode) { + private void makeRefLambda(ICodeWriter code, InvokeCustomNode customNode) throws CodegenException { InsnNode callInsn = customNode.getCallInsn(); if (callInsn instanceof ConstructorInsn) { MethodInfo callMth = ((ConstructorInsn) callInsn).getCallMth(); @@ -950,7 +950,7 @@ public class InsnGen { if (customNode.getHandleType() == MethodHandleType.INVOKE_STATIC) { useClass(code, callMth.getDeclClass()); } else { - code.add("this"); + addArg(code, customNode.getArg(0)); } code.add("::").add(callMth.getAlias()); } diff --git a/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance3.java b/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance3.java new file mode 100644 index 000000000..9d7666b38 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaInstance3.java @@ -0,0 +1,54 @@ +package jadx.tests.integration.java8; + +import java.util.function.Supplier; + +import org.junit.jupiter.api.Test; + +import jadx.tests.api.RaungTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +@SuppressWarnings("DataFlowIssue") +public class TestLambdaInstance3 extends RaungTest { + + public interface TestCls extends Supplier { + default TestCls test() { + return (TestCls & Memoized) Lazy.of(this)::get; + } + } + + public static final class Lazy implements Supplier { + public static Lazy of(Supplier supplier) { + return null; + } + + @Override + public T get() { + return null; + } + } + + interface Memoized { + } + + @Test + public void test() { + // some java versions failed to compile usage of interface with '$' in name + addClsRename("jadx.tests.integration.java8.TestLambdaInstance3$TestCls", "java8.TestCls"); + assertThat(getClassNode(TestCls.class)) + .code() + .doesNotContain("this::get") + .containsOne("return (TestCls) of::get;"); + // TODO: type inference set type for 'of' to Memoized and cast incorrectly removed + // .containsOne("Memoized)"); + } + + @Test + public void testRaung() { + disableCompilation(); + assertThat(getClassNodeFromRaung()) + .code() + .doesNotContain("this::get") + .containsOne(" of::get"); + } +} diff --git a/jadx-core/src/test/raung/java8/TestLambdaInstance3.raung b/jadx-core/src/test/raung/java8/TestLambdaInstance3.raung new file mode 100644 index 000000000..a394d830c --- /dev/null +++ b/jadx-core/src/test/raung/java8/TestLambdaInstance3.raung @@ -0,0 +1,45 @@ +.version 52 # Java 8 +.class public interface abstract java8/TestLambdaInstance3 +.implements java/io/Serializable +.implements java/util/function/Supplier +.signature Ljava/lang/Object;Ljava/io/Serializable;Ljava/util/function/Supplier; +.source "Function0.java" +.annotation runtime Ljava/lang/FunctionalInterface; +.end annotation + + +.method public memoized()Ljava8/TestLambdaInstance3; + .signature ()Ljava8/TestLambdaInstance3; + .max stack 2 + .max locals 1 + + .local 0 "this" Ljava8/TestLambdaInstance3; Ljava8/TestLambdaInstance3; + .line 197 + aload 0 + invokeinterface java8/TestLambdaInstance3 isMemoized ()Z + ifeq :L1 + .line 198 + aload 0 + areturn + :L1 + .line 200 + .stack same + aload 0 + invokestatic test/Lazy of (Ljava/util/function/Supplier;)Ltest/Lazy; + dup + invokevirtual java/lang/Object getClass ()Ljava/lang/Class; + pop + invokedynamic apply (Ltest/Lazy;)Ljava8/TestLambdaInstance3; + .handle invoke-static java/lang/invoke/LambdaMetafactory altMetafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite; + .arg 0 .methodtype ()Ljava/lang/Object; + .arg 1 .handle invoke-virtual test/Lazy get ()Ljava/lang/Object; + .arg 2 .methodtype ()Ljava/lang/Object; + .arg 3 7 + .arg 4 1 + .arg 5 Ltest/Memoized; + .arg 6 0 + .end invokedynamic + checkcast test/Memoized + checkcast java8/TestLambdaInstance3 + areturn +.end method diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ConstPoolReader.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ConstPoolReader.java index 232ea9635..0ca026bf2 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ConstPoolReader.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/data/ConstPoolReader.java @@ -221,6 +221,8 @@ public class ConstPoolReader { return new EncodedValue(EncodedType.ENCODED_LONG, data.readS8()); case DOUBLE: return new EncodedValue(EncodedType.ENCODED_DOUBLE, Double.longBitsToDouble(data.readU8())); + case CLASS: + return new EncodedValue(EncodedType.ENCODED_TYPE, getClass(idx)); case METHOD_TYPE: return new EncodedValue(EncodedType.ENCODED_METHOD_TYPE, DescriptorParser.parseToMethodProto(getUtf8(readU2()))); case METHOD_HANDLE: