fix: lambda decoding and code generation (#2139)

This commit is contained in:
Skylot
2024-04-03 20:10:00 +01:00
parent 43c082e4da
commit 0c1f830f94
4 changed files with 103 additions and 2 deletions
@@ -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());
}
@@ -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<R> extends Supplier<R> {
default TestCls<R> test() {
return (TestCls<R> & Memoized) Lazy.of(this)::get;
}
}
public static final class Lazy<T> implements Supplier<T> {
public static <T> Lazy<T> of(Supplier<? extends T> 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");
}
}
@@ -0,0 +1,45 @@
.version 52 # Java 8
.class public interface abstract java8/TestLambdaInstance3
.implements java/io/Serializable
.implements java/util/function/Supplier
.signature <R:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;Ljava/util/function/Supplier<TR;>;
.source "Function0.java"
.annotation runtime Ljava/lang/FunctionalInterface;
.end annotation
.method public memoized()Ljava8/TestLambdaInstance3;
.signature ()Ljava8/TestLambdaInstance3<TR;>;
.max stack 2
.max locals 1
.local 0 "this" Ljava8/TestLambdaInstance3; Ljava8/TestLambdaInstance3<TR;>;
.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
@@ -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: