From 69e40884a89c07c6aa57c2843d2de4e94d68bee7 Mon Sep 17 00:00:00 2001 From: Teletha Date: Thu, 25 Apr 2024 05:28:59 +0900 Subject: [PATCH] feat: (javac) support constructor reference --- bytecode.txt | 390 ++++++------------ .../reincarnation/JavaMethodDecompiler.java | 104 +++-- .../java/reincarnation/OperandCondition.java | 2 +- .../OperandConstructorReference.java | 53 +++ .../reincarnation/OperandLocalVariable.java | 2 +- .../java/reincarnation/OperandMethodCall.java | 16 +- .../reincarnation/OperandMethodReference.java | 6 +- src/main/java/reincarnation/coder/Coder.java | 7 + .../reincarnation/coder/DelegatableCoder.java | 8 + .../reincarnation/coder/java/JavaCoder.java | 8 + .../java/reincarnation/JavacEnvironment.java | 77 ---- .../decompiler/lambda/LambdaTest.java | 25 +- .../lambda/MethodReferenceTest.java | 27 +- 13 files changed, 307 insertions(+), 418 deletions(-) create mode 100644 src/main/java/reincarnation/OperandConstructorReference.java delete mode 100644 src/test/java/reincarnation/JavacEnvironment.java diff --git a/bytecode.txt b/bytecode.txt index 71cf940e..ac9b8ec3 100644 --- a/bytecode.txt +++ b/bytecode.txt @@ -1,317 +1,179 @@ -reincarnation.Failuer: Compile Error -================================================= -/reincarnation/decompiler/method/ExtendTest.java:29: エラー: クラス Parentのコンストラクタ Parentは指定された型に適用できません。 - Child(final ExtendTest$1 this$1) { - ^ - 期待値: reincarnation.decompiler.method.ExtendTest.ExtendTest$1 - 検出値: 引数がありません - 理由: 実引数リストと仮引数リストの長さが異なります - -------------------------------------------------- -01 package reincarnation.decompiler.method; -02 -03 import reincarnation.CodeVerifier; -04 import reincarnation.TestCode.Int; -05 import reincarnation.decompiler.method.ExtendTest; -06 -07 class ExtendTest extends CodeVerifier { -08 -09 class ExtendTest$1 implements Int { -10 -11 ExtendTest$1(final ExtendTest this$0) { -12 } -13 -14 public int run() { -15 -16 class Parent { -17 -18 Parent(final ExtendTest$1 this$1) { -19 } -20 -21 int value() { -22 return 10; -23 } -24 } -25 -26 -27 class Child extends Parent { -28 -29 Child(final ExtendTest$1 this$1) { -30 } -31 } -32 -33 return new Child(this).value(); -34 } -35 } -36 } -================================================= - at reincarnation.Failuer.type(Failuer.java:38) - at reincarnation.CodeVerifier.verify(CodeVerifier.java:183) - at reincarnation.decompiler.method.ExtendTest.extendClass(ExtendTest.java:24) +java.lang.NoSuchMethodException: reincarnation.decompiler.lambda.MethodReferenceTest$ConstructorReference.() + at java.base/java.lang.Class.getDeclaredMethod(Class.java:2848) + at reincarnation.JavaMethodDecompiler.visitInvokeDynamicInsn(JavaMethodDecompiler.java:1401) + at org.objectweb.asm.ClassReader.readCode(ClassReader.java:2473) + at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1512) + at org.objectweb.asm.ClassReader.accept(ClassReader.java:745) + at org.objectweb.asm.ClassReader.accept(ClassReader.java:425) + at reincarnation.Reincarnation.lambda$1(Reincarnation.java:168) + at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) + at reincarnation.Reincarnation.exhume(Reincarnation.java:161) + at reincarnation.Reincarnation.rebirth(Reincarnation.java:187) + at reincarnation.CodeVerifier.decompile(CodeVerifier.java:252) + at reincarnation.CodeVerifier.verify(CodeVerifier.java:203) + at reincarnation.decompiler.lambda.MethodReferenceTest.constructorReference(MethodReferenceTest.java:47) at java.base/java.lang.reflect.Method.invoke(Method.java:580) - at antibug.powerassert.PowerAssert.capture(PowerAssert.java:62) at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - Suppressed: Fail compiling code. -No solution. Suppressed: reincarnation.Failuer: Fail to cross-decompile. ----------------------------------------------------------------------- - Javac version - reincarnation.decompiler.method.ExtendTest$1$1Parent ------------------------------------------------------------------------ -public class ExtendTest$1$1ParentDump implements Opcodes { - -public static byte[] dump () throws Exception { - -classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/method/ExtendTest$1$1Parent", null, "java/lang/Object", null); - -{ -methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", null, null); -methodVisitor.visitParameter("NoParameterName", ACC_FINAL | ACC_MANDATED); -methodVisitor.visitCode(); -Label label0 = new Label(); -methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(28, label0); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); -methodVisitor.visitInsn(RETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1$1Parent;", null, label0, label1, 0); -methodVisitor.visitLocalVariable("this$1", "Lreincarnation/decompiler/method/ExtendTest$1;", null, label0, label1, 1); -methodVisitor.visitMaxs(1, 2); -methodVisitor.visitEnd(); -} -{ -methodVisitor = classWriter.visitMethod(0, "value", "()I", null, null); -methodVisitor.visitCode(); -Label label0 = new Label(); -methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(30, label0); -methodVisitor.visitIntInsn(BIPUSH, 10); -methodVisitor.visitInsn(IRETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1$1Parent;", null, label0, label1, 0); -methodVisitor.visitMaxs(1, 1); -methodVisitor.visitEnd(); -} - -} -} ------------------------------------------------------------------------ - ECJ version - reincarnation.decompiler.method.ExtendTest$1$1Parent + Javac version - reincarnation.decompiler.lambda.MethodReferenceTest$2 ----------------------------------------------------------------------- -public class ExtendTest$1$1ParentDump implements Opcodes { +public class MethodReferenceTest$2Dump implements Opcodes { public static byte[] dump () throws Exception { -classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/method/ExtendTest$1$1Parent", null, "java/lang/Object", null); +classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/lambda/MethodReferenceTest$2", null, "java/lang/Object", new String[] { "reincarnation/TestCode$Run" }); { -fieldVisitor = classWriter.visitField(ACC_FINAL | ACC_SYNTHETIC, "this$1", "Lreincarnation/decompiler/method/ExtendTest$1;", null, null); +fieldVisitor = classWriter.visitField(ACC_FINAL | ACC_STATIC | ACC_SYNTHETIC, "$assertionsDisabled", "Z", null, null); fieldVisitor.visitEnd(); } { -methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", null, null); -methodVisitor.visitParameter("this$0", ACC_FINAL | ACC_MANDATED); +methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/lambda/MethodReferenceTest;)V", null, null); +methodVisitor.visitParameter("NoParameterName", ACC_FINAL | ACC_MANDATED); methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(28, label0); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitVarInsn(ALOAD, 1); -methodVisitor.visitFieldInsn(PUTFIELD, "reincarnation/decompiler/method/ExtendTest$1$1Parent", "this$1", "Lreincarnation/decompiler/method/ExtendTest$1;"); +methodVisitor.visitLineNumber(47, label0); methodVisitor.visitVarInsn(ALOAD, 0); methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); methodVisitor.visitInsn(RETURN); Label label1 = new Label(); methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1$1Parent;", null, label0, label1, 0); -methodVisitor.visitMaxs(2, 2); +methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/lambda/MethodReferenceTest$2;", null, label0, label1, 0); +methodVisitor.visitLocalVariable("this$0", "Lreincarnation/decompiler/lambda/MethodReferenceTest;", null, label0, label1, 1); +methodVisitor.visitMaxs(1, 2); methodVisitor.visitEnd(); } { -methodVisitor = classWriter.visitMethod(0, "value", "()I", null, null); +methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()V", null, null); methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(30, label0); -methodVisitor.visitIntInsn(BIPUSH, 10); -methodVisitor.visitInsn(IRETURN); +methodVisitor.visitLineNumber(51, label0); +methodVisitor.visitInvokeDynamicInsn("get", "()Ljava/util/function/Supplier;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getType("()Ljava/lang/Object;"), new Handle(Opcodes.H_NEWINVOKESPECIAL, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference", "", "()V", false), Type.getType("()Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;")}); +methodVisitor.visitVarInsn(ASTORE, 1); Label label1 = new Label(); methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1$1Parent;", null, label0, label1, 0); -methodVisitor.visitMaxs(1, 1); -methodVisitor.visitEnd(); -} - -} -} ------------------------------------------------------------------------ - Javac version - reincarnation.decompiler.method.ExtendTest$1$1Child ------------------------------------------------------------------------ -public class ExtendTest$1$1ChildDump implements Opcodes { - -public static byte[] dump () throws Exception { - -classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/method/ExtendTest$1$1Child", null, "reincarnation/decompiler/method/ExtendTest$1$1Parent", null); - -{ -methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", null, null); -methodVisitor.visitParameter("NoParameterName", ACC_FINAL | ACC_MANDATED); -methodVisitor.visitCode(); -Label label0 = new Label(); -methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(34, label0); -methodVisitor.visitVarInsn(ALOAD, 0); +methodVisitor.visitLineNumber(52, label1); methodVisitor.visitVarInsn(ALOAD, 1); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "reincarnation/decompiler/method/ExtendTest$1$1Parent", "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", false); -methodVisitor.visitInsn(RETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1$1Child;", null, label0, label1, 0); -methodVisitor.visitLocalVariable("this$1", "Lreincarnation/decompiler/method/ExtendTest$1;", null, label0, label1, 1); -methodVisitor.visitMaxs(2, 2); -methodVisitor.visitEnd(); -} - -} -} ------------------------------------------------------------------------ - ECJ version - reincarnation.decompiler.method.ExtendTest$1$1Child ------------------------------------------------------------------------ -public class ExtendTest$1$1ChildDump implements Opcodes { - -public static byte[] dump () throws Exception { - -classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/method/ExtendTest$1$1Child", null, "reincarnation/decompiler/method/ExtendTest$1$1Parent", null); +methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Supplier", "get", "()Ljava/lang/Object;", true); +methodVisitor.visitTypeInsn(CHECKCAST, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"); +methodVisitor.visitVarInsn(ASTORE, 2); +Label label2 = new Label(); +methodVisitor.visitLabel(label2); +methodVisitor.visitLineNumber(54, label2); +methodVisitor.visitFieldInsn(GETSTATIC, "reincarnation/decompiler/lambda/MethodReferenceTest$2", "$assertionsDisabled", "Z"); +Label label3 = new Label(); +methodVisitor.visitJumpInsn(IFNE, label3); +methodVisitor.visitVarInsn(ALOAD, 2); +methodVisitor.visitFieldInsn(GETFIELD, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference", "value", "I"); +methodVisitor.visitInsn(ICONST_M1); +methodVisitor.visitJumpInsn(IF_ICMPEQ, label3); +methodVisitor.visitTypeInsn(NEW, "java/lang/AssertionError"); +methodVisitor.visitInsn(DUP); -{ -fieldVisitor = classWriter.visitField(ACC_FINAL | ACC_SYNTHETIC, "this$1", "Lreincarnation/decompiler/method/ExtendTest$1;", null, null); -fieldVisitor.visitEnd(); -} -{ -methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", null, null); -methodVisitor.visitParameter("this$0", ACC_FINAL | ACC_MANDATED); -methodVisitor.visitCode(); -Label label0 = new Label(); -methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(34, label0); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitVarInsn(ALOAD, 1); -methodVisitor.visitFieldInsn(PUTFIELD, "reincarnation/decompiler/method/ExtendTest$1$1Child", "this$1", "Lreincarnation/decompiler/method/ExtendTest$1;"); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitVarInsn(ALOAD, 1); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "reincarnation/decompiler/method/ExtendTest$1$1Parent", "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", false); -methodVisitor.visitInsn(RETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1$1Child;", null, label0, label1, 0); -methodVisitor.visitMaxs(2, 2); -methodVisitor.visitEnd(); -} -} -} ------------------------------------------------------------------------ - Javac version - reincarnation.decompiler.method.ExtendTest$1 ----------------------------------------------------------------------- -public class ExtendTest$1Dump implements Opcodes { + ECJ version - reincarnation.decompiler.lambda.MethodReferenceTest$2 -public static byte[] dump () throws Exception { - -classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/method/ExtendTest$1", null, "java/lang/Object", new String[] { "reincarnation/TestCode$Int" }); - -{ -methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/method/ExtendTest;)V", null, null); -methodVisitor.visitParameter("NoParameterName", ACC_FINAL | ACC_MANDATED); -methodVisitor.visitCode(); -Label label0 = new Label(); -methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(24, label0); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); -methodVisitor.visitInsn(RETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1;", null, label0, label1, 0); -methodVisitor.visitLocalVariable("this$0", "Lreincarnation/decompiler/method/ExtendTest;", null, label0, label1, 1); -methodVisitor.visitMaxs(1, 2); -methodVisitor.visitEnd(); -} -{ -methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()I", null, null); -methodVisitor.visitCode(); -Label label0 = new Label(); methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(37, label0); -methodVisitor.visitTypeInsn(NEW, "reincarnation/decompiler/method/ExtendTest$1$1Child"); -methodVisitor.visitInsn(DUP); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "reincarnation/decompiler/method/ExtendTest$1$1Child", "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", false); -methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "reincarnation/decompiler/method/ExtendTest$1$1Child", "value", "()I", false); -methodVisitor.visitInsn(IRETURN); +methodVisitor.visitLineNumber(51, label0); +上がJavac 下がECJ +methodVisitor.visitInvokeDynamicInsn("get", "()Ljava/util/function/Supplier;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getType("()Ljava/lang/Object;"), new Handle(Opcodes.H_NEWINVOKESPECIAL, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference", "", "()V", false), Type.getType("()Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;")}); +methodVisitor.visitInvokeDynamicInsn("get", "()Ljava/util/function/Supplier;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getType("()Ljava/lang/Object;"), new Handle(Opcodes.H_INVOKESTATIC, "reincarnation/decompiler/lambda/MethodReferenceTest$2", "lambda$0", "()Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;", false), Type.getType("()Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;")}); +methodVisitor.visitVarInsn(ASTORE, 1); Label label1 = new Label(); methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1;", null, label0, label1, 0); -methodVisitor.visitMaxs(3, 1); -methodVisitor.visitEnd(); -} - -} -} ------------------------------------------------------------------------ - ECJ version - reincarnation.decompiler.method.ExtendTest$1 ------------------------------------------------------------------------ -public class ExtendTest$1Dump implements Opcodes { - -public static byte[] dump () throws Exception { - -classWriter.visit(V21, ACC_SUPER, "reincarnation/decompiler/method/ExtendTest$1", null, "java/lang/Object", new String[] { "reincarnation/TestCode$Int" }); - -{ -fieldVisitor = classWriter.visitField(ACC_FINAL | ACC_SYNTHETIC, "this$0", "Lreincarnation/decompiler/method/ExtendTest;", null, null); -fieldVisitor.visitEnd(); -} -{ -methodVisitor = classWriter.visitMethod(0, "", "(Lreincarnation/decompiler/method/ExtendTest;)V", null, null); -methodVisitor.visitParameter("this$0", ACC_FINAL | ACC_MANDATED); -methodVisitor.visitCode(); -Label label0 = new Label(); -methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(24, label0); -methodVisitor.visitVarInsn(ALOAD, 0); +methodVisitor.visitLineNumber(52, label1); methodVisitor.visitVarInsn(ALOAD, 1); -methodVisitor.visitFieldInsn(PUTFIELD, "reincarnation/decompiler/method/ExtendTest$1", "this$0", "Lreincarnation/decompiler/method/ExtendTest;"); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); +methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Supplier", "get", "()Ljava/lang/Object;", true); +methodVisitor.visitTypeInsn(CHECKCAST, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"); +methodVisitor.visitVarInsn(ASTORE, 2); +Label label2 = new Label(); +methodVisitor.visitLabel(label2); +methodVisitor.visitLineNumber(54, label2); +methodVisitor.visitFieldInsn(GETSTATIC, "reincarnation/decompiler/lambda/MethodReferenceTest", "$assertionsDisabled", "Z"); +Label label3 = new Label(); +methodVisitor.visitJumpInsn(IFNE, label3); +methodVisitor.visitVarInsn(ALOAD, 2); +methodVisitor.visitFieldInsn(GETFIELD, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference", "value", "I"); +methodVisitor.visitInsn(ICONST_M1); +methodVisitor.visitJumpInsn(IF_ICMPEQ, label3); +methodVisitor.visitTypeInsn(NEW, "java/lang/AssertionError"); +methodVisitor.visitInsn(DUP); +methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "", "()V", false); +methodVisitor.visitInsn(ATHROW); +methodVisitor.visitLabel(label3); +methodVisitor.visitLineNumber(55, label3); +methodVisitor.visitFrame(Opcodes.F_NEW, 3, new Object[] {"reincarnation/decompiler/lambda/MethodReferenceTest$2", "java/util/function/Supplier", "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"}, 0, new Object[] {}); +methodVisitor.visitFieldInsn(GETSTATIC, "reincarnation/decompiler/lambda/MethodReferenceTest", "$assertionsDisabled", "Z"); +Label label4 = new Label(); +methodVisitor.visitJumpInsn(IFNE, label4); +methodVisitor.visitVarInsn(ALOAD, 2); +methodVisitor.visitTypeInsn(INSTANCEOF, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"); +methodVisitor.visitJumpInsn(IFNE, label4); +methodVisitor.visitTypeInsn(NEW, "java/lang/AssertionError"); +methodVisitor.visitInsn(DUP); +methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "", "()V", false); +methodVisitor.visitInsn(ATHROW); +methodVisitor.visitLabel(label4); +methodVisitor.visitLineNumber(56, label4); +methodVisitor.visitFrame(Opcodes.F_NEW, 3, new Object[] {"reincarnation/decompiler/lambda/MethodReferenceTest$2", "java/util/function/Supplier", "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"}, 0, new Object[] {}); +methodVisitor.visitFieldInsn(GETSTATIC, "reincarnation/decompiler/lambda/MethodReferenceTest", "$assertionsDisabled", "Z"); +Label label5 = new Label(); +methodVisitor.visitJumpInsn(IFNE, label5); +methodVisitor.visitVarInsn(ALOAD, 2); +methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); +methodVisitor.visitLdcInsn(Type.getType("Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;")); +methodVisitor.visitJumpInsn(IF_ACMPEQ, label5); +methodVisitor.visitTypeInsn(NEW, "java/lang/AssertionError"); +methodVisitor.visitInsn(DUP); +methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "", "()V", false); +methodVisitor.visitInsn(ATHROW); +methodVisitor.visitLabel(label5); +methodVisitor.visitLineNumber(57, label5); +methodVisitor.visitFrame(Opcodes.F_NEW, 3, new Object[] {"reincarnation/decompiler/lambda/MethodReferenceTest$2", "java/util/function/Supplier", "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"}, 0, new Object[] {}); +methodVisitor.visitFieldInsn(GETSTATIC, "reincarnation/decompiler/lambda/MethodReferenceTest", "$assertionsDisabled", "Z"); +Label label6 = new Label(); +methodVisitor.visitJumpInsn(IFNE, label6); +methodVisitor.visitLdcInsn(Type.getType("Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;")); +methodVisitor.visitVarInsn(ALOAD, 2); +methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); +methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "isAssignableFrom", "(Ljava/lang/Class;)Z", false); +methodVisitor.visitJumpInsn(IFNE, label6); +methodVisitor.visitTypeInsn(NEW, "java/lang/AssertionError"); +methodVisitor.visitInsn(DUP); +methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/AssertionError", "", "()V", false); +methodVisitor.visitInsn(ATHROW); +methodVisitor.visitLabel(label6); +methodVisitor.visitLineNumber(58, label6); +methodVisitor.visitFrame(Opcodes.F_NEW, 3, new Object[] {"reincarnation/decompiler/lambda/MethodReferenceTest$2", "java/util/function/Supplier", "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"}, 0, new Object[] {}); methodVisitor.visitInsn(RETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1;", null, label0, label1, 0); -methodVisitor.visitMaxs(2, 2); +Label label7 = new Label(); +methodVisitor.visitLabel(label7); +methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/lambda/MethodReferenceTest$2;", null, label0, label7, 0); +methodVisitor.visitLocalVariable("function", "Ljava/util/function/Supplier;", "Ljava/util/function/Supplier;", label1, label7, 1); +methodVisitor.visitLocalVariable("instance", "Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;", null, label2, label7, 2); +methodVisitor.visitMaxs(2, 3); methodVisitor.visitEnd(); } { -methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "run", "()I", null, null); +methodVisitor = classWriter.visitMethod(ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, "lambda$0", "()Lreincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference;", null, null); methodVisitor.visitCode(); Label label0 = new Label(); methodVisitor.visitLabel(label0); -methodVisitor.visitLineNumber(37, label0); -methodVisitor.visitTypeInsn(NEW, "reincarnation/decompiler/method/ExtendTest$1$1Child"); +methodVisitor.visitLineNumber(1, label0); +methodVisitor.visitTypeInsn(NEW, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference"); methodVisitor.visitInsn(DUP); -methodVisitor.visitVarInsn(ALOAD, 0); -methodVisitor.visitMethodInsn(INVOKESPECIAL, "reincarnation/decompiler/method/ExtendTest$1$1Child", "", "(Lreincarnation/decompiler/method/ExtendTest$1;)V", false); -methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "reincarnation/decompiler/method/ExtendTest$1$1Child", "value", "()I", false); -methodVisitor.visitInsn(IRETURN); -Label label1 = new Label(); -methodVisitor.visitLabel(label1); -methodVisitor.visitLocalVariable("this", "Lreincarnation/decompiler/method/ExtendTest$1;", null, label0, label1, 0); -methodVisitor.visitMaxs(3, 1); +methodVisitor.visitMethodInsn(INVOKESPECIAL, "reincarnation/decompiler/lambda/MethodReferenceTest$ConstructorReference", "", "()V", false); +methodVisitor.visitInsn(ARETURN); +methodVisitor.visitMaxs(2, 0); methodVisitor.visitEnd(); } @@ -319,5 +181,5 @@ methodVisitor.visitEnd(); } at reincarnation.Failuer.type(Failuer.java:38) at reincarnation.CodeVerifier.verify(CodeVerifier.java:226) - ... 9 more + ... 8 more diff --git a/src/main/java/reincarnation/JavaMethodDecompiler.java b/src/main/java/reincarnation/JavaMethodDecompiler.java index b00d45ce..4771ee55 100644 --- a/src/main/java/reincarnation/JavaMethodDecompiler.java +++ b/src/main/java/reincarnation/JavaMethodDecompiler.java @@ -14,6 +14,7 @@ import static reincarnation.OperandCondition.*; import static reincarnation.OperandUtil.*; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -936,18 +937,23 @@ public void visitInsn(int opcode) { break; case POP: - // When the JDK compiler compiles the code including "instance method reference", it + // When the compiler compiles the code including "instance method reference", it // generates the byte code expressed in following ASM codes. // + // In Javac + // visitInsn(DUP); + // visitMethodInsn(INVOKESTATIC, "java/util/Objects","requireNonNull",SIGNATURE, false); + // visitInsn(POP); + // + // In ECJ // visitInsn(DUP); // visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object","getClass","()Ljava/lang/Class;"); // visitInsn(POP); // - // Although i guess that it is the initialization code for the class to - // which the lambda method belongs, ECJ doesn't generated such code. - // In Javascript runtime, it is a completely unnecessary code, - // so we should delete them unconditionally. - if (match(DUP, INVOKEVIRTUAL, POP)) { + // I guess that it is NULL checker and initialize code for the class to which the lambda + // method belongs. It is a completely unnecessary code, so we should remove them + // unconditionally. + if (match(DUP, INVOKESTATIC, POP) || match(DUP, INVOKEVIRTUAL, POP)) { current.remove(0); break; } @@ -1389,60 +1395,70 @@ public void visitInvokeDynamicInsn(String name, String description, Handle boots // detect functional interface Class interfaceClass = load(callerType.getReturnType()); - // detect lambda method try { + // detect lambda method Class lambdaClass = load(handle.getOwner()); + String lambdaName = handle.getName(); Class[] lambdaParameterTypes = load(Type.getArgumentTypes(handle.getDesc())); - Method lambdaMethod = lambdaClass.getDeclaredMethod(handle.getName(), lambdaParameterTypes); + boolean needInstance = callerType.getArgumentTypes().length != 0; - if (lambdaMethod.isSynthetic()) { - // ================================== - // Lambda - // ================================== - // build parameter from local environment - int consumableStackSize = callerType.getArgumentTypes().length; - List params = new ArrayList(); - - for (int i = consumableStackSize - 1; 0 <= i; i--) { - Operand removed = current.remove(i); - if (removed instanceof OperandLocalVariable local && local.index == 0) { - // ignore "this" variable - } else { - params.add(removed); - } - } + source.require(lambdaClass); - current.addOperand(new OperandLambda(interfaceClass, lambdaMethod, params, source)); - } else { + switch (handle.getTag()) { + case H_INVOKESTATIC: + case H_INVOKEVIRTUAL: + case H_INVOKEINTERFACE: // ================================== // Method Reference // ================================== - source.require(lambdaClass); - - boolean needInstance = callerType.getArgumentTypes().length != 0; - - switch (handle.getTag()) { - case H_INVOKESTATIC: - case H_INVOKEVIRTUAL: - case H_INVOKEINTERFACE: + Method lambdaMethod = lambdaClass.getDeclaredMethod(lambdaName, lambdaParameterTypes); + if (lambdaMethod.isSynthetic()) { + // ================================== + // Lambda + // ================================== + // build parameter from local environment + int consumableStackSize = callerType.getArgumentTypes().length; + List params = new ArrayList(); + + for (int i = consumableStackSize - 1; 0 <= i; i--) { + Operand removed = current.remove(i); + if (removed instanceof OperandLocalVariable local && local.index == 0) { + // ignore "this" variable + } else { + params.add(removed); + } + } + current.addOperand(new OperandLambda(interfaceClass, lambdaMethod, params, source)); + } else { SpecializedType specialized = new SpecializedType(interfaceClass) .specializeByReturnAndParamTypes((Type) bootstrapMethodArguments[2]) .specializeBySAM(lambdaMethod); current.addOperand(new OperandMethodReference(lambdaMethod, needInstance ? current.remove(0) : null) .fix(specialized)); - break; + } + break; - case H_INVOKESPECIAL: - case H_NEWINVOKESPECIAL: - break; + case H_INVOKESPECIAL: + break; - default: - // If this exception will be thrown, it is bug of this program. So we must - // rethrow - // the wrapped error in here. - throw new Error(); - } + case H_NEWINVOKESPECIAL: + // ================================== + // Constructor Reference in Javac + // ================================== + Constructor lambdaConstructor = lambdaClass.getDeclaredConstructor(lambdaParameterTypes); + SpecializedType specialized = new SpecializedType(interfaceClass).specializeByReturnType(lambdaClass) + .specializeByParamTypes(lambdaParameterTypes); + + current.addOperand(new OperandConstructorReference(lambdaConstructor).fix(specialized)); + break; + + default: + // If this exception will be thrown, it is bug of this program. So we + // must + // rethrow + // the wrapped error in here. + throw new Error(); } } catch (Exception e) { throw I.quiet(e); diff --git a/src/main/java/reincarnation/OperandCondition.java b/src/main/java/reincarnation/OperandCondition.java index bdb60ef0..f09ddbb9 100644 --- a/src/main/java/reincarnation/OperandCondition.java +++ b/src/main/java/reincarnation/OperandCondition.java @@ -289,6 +289,6 @@ protected boolean isValue() { */ @Override protected String view() { - return "if (" + left.view() + " " + right.view() + ") then " + then.id + " else " + elze.id; + return "if (" + left.view() + " " + right.view() + ") then " + then.id + " else " + (elze == null ? "SAME" : elze.id); } } \ No newline at end of file diff --git a/src/main/java/reincarnation/OperandConstructorReference.java b/src/main/java/reincarnation/OperandConstructorReference.java new file mode 100644 index 00000000..25c749c8 --- /dev/null +++ b/src/main/java/reincarnation/OperandConstructorReference.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The REINCARNATION Development Team + * + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/MIT + */ +package reincarnation; + +import java.lang.reflect.Constructor; + +import reincarnation.coder.Coder; + +class OperandConstructorReference extends Operand { + + /** The lambda body. */ + private final Constructor reference; + + /** + * Create the operand for constructor reference. + * + * @param reference + */ + OperandConstructorReference(Constructor reference) { + this.reference = reference; + } + + /** + * {@inheritDoc} + */ + @Override + protected void writeCode(Coder coder) { + coder.writeConstructorReference(reference); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean isValue() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + protected String view() { + return reference.getName() + "::new"; + } +} \ No newline at end of file diff --git a/src/main/java/reincarnation/OperandLocalVariable.java b/src/main/java/reincarnation/OperandLocalVariable.java index 90f3fb36..cee68563 100644 --- a/src/main/java/reincarnation/OperandLocalVariable.java +++ b/src/main/java/reincarnation/OperandLocalVariable.java @@ -62,7 +62,7 @@ protected boolean isNegatable() { * {@inheritDoc} */ @Override - public boolean isValue() { + protected boolean isValue() { return true; } diff --git a/src/main/java/reincarnation/OperandMethodCall.java b/src/main/java/reincarnation/OperandMethodCall.java index 1a0f6fd6..55e9ef80 100644 --- a/src/main/java/reincarnation/OperandMethodCall.java +++ b/src/main/java/reincarnation/OperandMethodCall.java @@ -13,11 +13,12 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; -import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import kiss.I; import kiss.Model; @@ -80,7 +81,14 @@ class OperandMethodCall extends Operand { * @return */ private Method find(Class owner, String name, Class[] types) { - for (Class type : Model.collectTypes(owner)) { + Set collections = Model.collectTypes(owner); + + // In Javac, when the interface method reference invokes the Object class method, + // it is compiled as a call to the interface, not to the object. However, this would fail to + // find methods of the Object class, so the Object class is added unconditionally. + collections.add(Object.class); + + for (Class type : collections) { try { Method method = type.getDeclaredMethod(name, types); int mod = method.getModifiers(); @@ -98,7 +106,9 @@ private Method find(Class owner, String name, Class[] types) { // ignore } } - throw new NoSuchMethodError(owner + "#" + name + Arrays.toString(types)); + throw new NoSuchMethodError(owner + "#" + name + Stream.of(types) + .map(Class::getSimpleName) + .collect(Collectors.joining(", ", "(", ")"))); } /** diff --git a/src/main/java/reincarnation/OperandMethodReference.java b/src/main/java/reincarnation/OperandMethodReference.java index f07cd980..525fb485 100644 --- a/src/main/java/reincarnation/OperandMethodReference.java +++ b/src/main/java/reincarnation/OperandMethodReference.java @@ -57,6 +57,10 @@ protected boolean isValue() { */ @Override protected String view() { - return context.view() + "::" + reference.getName(); + if (context == null) { + return reference.getDeclaringClass().getSimpleName() + "::" + reference.getName(); + } else { + return context.view() + "::" + reference.getName(); + } } } \ No newline at end of file diff --git a/src/main/java/reincarnation/coder/Coder.java b/src/main/java/reincarnation/coder/Coder.java index f6df74b9..8fd75910 100644 --- a/src/main/java/reincarnation/coder/Coder.java +++ b/src/main/java/reincarnation/coder/Coder.java @@ -583,6 +583,13 @@ public final void writeEnclose(Code code) { */ public abstract void writeThisConstructorCall(Constructor constructor, List params); + /** + * Constructor reference. + * + * @param constructor A constructor info. + */ + public abstract void writeConstructorReference(Constructor constructor); + /** * Method call expression. * diff --git a/src/main/java/reincarnation/coder/DelegatableCoder.java b/src/main/java/reincarnation/coder/DelegatableCoder.java index a438076b..d4496fc4 100644 --- a/src/main/java/reincarnation/coder/DelegatableCoder.java +++ b/src/main/java/reincarnation/coder/DelegatableCoder.java @@ -345,6 +345,14 @@ public void writeConstructorCall(Constructor constructor, List params) { coder.writeConstructorCall(constructor, params); } + /** + * {@inheritDoc} + */ + @Override + public void writeConstructorReference(Constructor constructor) { + coder.writeConstructorReference(constructor); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/reincarnation/coder/java/JavaCoder.java b/src/main/java/reincarnation/coder/java/JavaCoder.java index 3da3d7dc..d2cb3ad3 100644 --- a/src/main/java/reincarnation/coder/java/JavaCoder.java +++ b/src/main/java/reincarnation/coder/java/JavaCoder.java @@ -753,6 +753,14 @@ public void writeThisConstructorCall(Constructor constructor, List params) } } + /** + * {@inheritDoc} + */ + @Override + public void writeConstructorReference(Constructor constructor) { + write(name(constructor.getDeclaringClass()), "::new"); + } + /** * {@inheritDoc} */ diff --git a/src/test/java/reincarnation/JavacEnvironment.java b/src/test/java/reincarnation/JavacEnvironment.java deleted file mode 100644 index 453bc826..00000000 --- a/src/test/java/reincarnation/JavacEnvironment.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2024 Nameless Production Committee - * - * Licensed under the MIT License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/mit-license.php - */ -package reincarnation; - -import bee.UserInterface; -import bee.api.Command; -import bee.util.JavaCompiler; -import psychopath.Locator; - -public class JavacEnvironment { - - public static void main(String[] args) { - // compile source by javac - Silent silent = new Silent(); - - ClassLoader compiled = JavaCompiler.with(silent) - .addCurrentClassPath() - .addSourceDirectory(Locator.directory("src/test/java")) - .compile(); - - System.out.println(silent.message); - } - - /** - * - */ - private static class Silent extends UserInterface { - - /** The message buffer. */ - private StringBuilder message = new StringBuilder(); - - /** - * {@inheritDoc} - */ - @Override - protected void write(int type, String message) { - this.message.append(message).append("\r\n"); - } - - /** - * {@inheritDoc} - */ - @Override - protected void write(Throwable error) { - error.printStackTrace(); - } - - /** - * {@inheritDoc} - */ - @Override - public Appendable getInterface() { - return System.out; - } - - /** - * {@inheritDoc} - */ - @Override - public void startCommand(String name, Command command) { - } - - /** - * {@inheritDoc} - */ - @Override - public void endCommand(String name, Command command) { - } - } -} diff --git a/src/test/java/reincarnation/decompiler/lambda/LambdaTest.java b/src/test/java/reincarnation/decompiler/lambda/LambdaTest.java index bd49de09..17c1b3b4 100644 --- a/src/test/java/reincarnation/decompiler/lambda/LambdaTest.java +++ b/src/test/java/reincarnation/decompiler/lambda/LambdaTest.java @@ -17,14 +17,13 @@ import java.util.function.IntUnaryOperator; import java.util.function.LongSupplier; -import org.junit.jupiter.api.Test; - import reincarnation.CodeVerifier; +import reincarnation.CrossDecompilerTest; import reincarnation.TestCode; class LambdaTest extends CodeVerifier { - @Test + @CrossDecompilerTest void inlineWithoutArg() { verify(new TestCode.Int() { @@ -39,7 +38,7 @@ private int lambda(IntSupplier supplier) { }); } - @Test + @CrossDecompilerTest void inlineWithArg() { verify(new TestCode.Int() { @@ -54,7 +53,7 @@ private int lambda(IntUnaryOperator supplier) { }); } - @Test + @CrossDecompilerTest void inlineWithArgs() { verify(new TestCode.Int() { @@ -69,7 +68,7 @@ private int lambda(IntBinaryOperator supplier) { }); } - @Test + @CrossDecompilerTest void inlineWithObject() { verify(new TestCode.Text() { @@ -84,7 +83,7 @@ private String lambda(Function function) { }); } - @Test + @CrossDecompilerTest void inlineWithParamiterizedType() { verify(new TestCode.Text() { @@ -99,7 +98,7 @@ private List lambda(Function, List> function) { }); } - @Test + @CrossDecompilerTest void useLocalVariable() { verify(new TestCode.Int() { @@ -116,7 +115,7 @@ private int lambda(IntSupplier supplier) { }); } - @Test + @CrossDecompilerTest void useLocalVariables() { verify(new TestCode.Long() { @@ -134,7 +133,7 @@ private long lambda(LongSupplier supplier) { }); } - @Test + @CrossDecompilerTest void useLocalVariableWithArg() { verify(new TestCode.Text() { @@ -151,7 +150,7 @@ private String lambda(Function function) { }); } - @Test + @CrossDecompilerTest void useLocalVariablesWithArg() { verify(new TestCode.Text() { @@ -169,7 +168,7 @@ private String lambda(Function function) { }); } - @Test + @CrossDecompilerTest void useLocalVariablesWithArgs() { verify(new TestCode.Text() { @@ -187,7 +186,7 @@ private String lambda(BiFunction function) { }); } - @Test + @CrossDecompilerTest void useLocalVariableWithArgNest() { verify(new TestCode.Int() { diff --git a/src/test/java/reincarnation/decompiler/lambda/MethodReferenceTest.java b/src/test/java/reincarnation/decompiler/lambda/MethodReferenceTest.java index e918a4ec..98eebe30 100644 --- a/src/test/java/reincarnation/decompiler/lambda/MethodReferenceTest.java +++ b/src/test/java/reincarnation/decompiler/lambda/MethodReferenceTest.java @@ -20,14 +20,13 @@ import java.util.function.Supplier; import java.util.function.ToIntFunction; -import org.junit.jupiter.api.Test; - import reincarnation.CodeVerifier; +import reincarnation.CrossDecompilerTest; import reincarnation.TestCode; class MethodReferenceTest extends CodeVerifier { - @Test + @CrossDecompilerTest void arrayReference() { verify(new TestCode.Run() { @@ -43,7 +42,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void constructorReference() { verify(new TestCode.Run() { @@ -60,7 +59,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void constructorReferenceWithParam() { verify(new TestCode.Run() { @@ -90,7 +89,7 @@ private ConstructorReference(int value) { } } - @Test + @CrossDecompilerTest void methodReference() { verify(new TestCode.Run() { @@ -107,7 +106,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void staticMethodReference() { verify(new TestCode.Run() { @@ -134,7 +133,7 @@ static long staticLongSupplier() { } } - @Test + @CrossDecompilerTest void supplierAsFunctionOnAbstractMethod() { verify(new TestCode.Run() { @@ -147,7 +146,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void supplierAsFunctionOnConcreteMethod() { verify(new TestCode.Run() { @@ -160,7 +159,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void functionAsBiFunction() { verify(new TestCode.Run() { @@ -173,7 +172,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void functionAsBiFunctionOnAbstractMethod() { verify(new TestCode.Run() { @@ -188,7 +187,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void functionAsBiFunctionOnConcreteMethod() { verify(new TestCode.Run() { @@ -206,7 +205,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void functionAsBiFunctionWithComplicatedType() { verify(new TestCode.Run() { @@ -221,7 +220,7 @@ public void run() { }); } - @Test + @CrossDecompilerTest void defaultMethod() { verify(new TestCode.Run() {