diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java index 7ec3e1ea47..14a28f92ea 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessor.java @@ -346,6 +346,11 @@ public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDe continue; } + if (MethodReferenceHelper.convertToMethodReference(root)) { + decompileRecord.add("ProcessLambda", root); + continue; + } + if (InlineSingleBlockHelper.inlineSingleBlocks(root)) { decompileRecord.add("InlineSingleBlocks", root); continue; diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/MethodReferenceHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/MethodReferenceHelper.java new file mode 100644 index 0000000000..8adedf4672 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/MethodReferenceHelper.java @@ -0,0 +1,260 @@ +package org.jetbrains.java.decompiler.modules.decompiler; + +import org.jetbrains.java.decompiler.code.CodeConstants; +import org.jetbrains.java.decompiler.code.FullInstructionSequence; +import org.jetbrains.java.decompiler.code.Instruction; +import org.jetbrains.java.decompiler.main.ClassesProcessor; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; +import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode.LambdaInformation; +import org.jetbrains.java.decompiler.main.DecompilerContext; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.consts.LinkConstant; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +import java.io.IOException; +import java.util.Iterator; + +public class MethodReferenceHelper { + public static boolean convertToMethodReference(RootStatement root) throws IOException { + return convertToMethodReferenceRec(root); + } + + public static boolean convertToMethodReferenceRec(Statement stat) throws IOException { + boolean result = false; + for (Statement subStat : stat.getStats()) { + result |= convertToMethodReferenceRec(subStat); + } + if (stat.getExprents() != null) { + for (int i = 0; i < stat.getExprents().size(); i++) { + Exprent exp = stat.getExprents().get(i); + for (Exprent subExp : exp.getAllExprents(true)) { + if (convertToMethodReference(stat, i, subExp)) { + result |= true; + } + for (int j = 0; j < stat.getExprents().size(); j++) { + if (stat.getExprents().get(j) == exp) { + i = j; + break; + } + } + if (removeInstanceAssignment(stat, i, subExp)) { + result |= true; + } + for (int j = 0; j < stat.getExprents().size(); j++) { + if (stat.getExprents().get(j) == exp) { + i = j; + break; + } + } + } + } + } + return result; + } + + private static boolean convertToMethodReference(Statement stat, int i, Exprent exp) throws IOException { + if (!(exp instanceof NewExprent newExprent) + || !newExprent.isLambda() + || newExprent.isMethodReference()) + return false; + String className = newExprent.getNewType().value; + ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className); + LambdaInformation info = node.lambdaInformation; + StructClass struct = DecompilerContext.getStructContext().getClass(info.content_class_name); + StructMethod method = struct.getMethod(info.content_method_key); + if (!method.hasModifier(CodeConstants.ACC_STATIC)) + return false; + method.expandData(struct); + FullInstructionSequence seq = method.getInstructionSequence(); + Iterator iterator = seq.iterator(); + int argument = 0; + + if (!iterator.hasNext()) + return false; + Instruction next = iterator.next(); + + // new instance + if (next.opcode == CodeConstants.opc_new) { + if (!iterator.hasNext()) + return false; + next = iterator.next(); + if (next.opcode != CodeConstants.opc_dup) + return false; + + if (!iterator.hasNext()) + return false; + next = iterator.next(); + } + + // Load arguments + while (iterator.hasNext() + && next.opcode >= CodeConstants.opc_iload && next.opcode <= CodeConstants.opc_aload + && next.operand(0) == argument) { + next = iterator.next(); + argument++; + } + + boolean varargs = false; + int varargsCount = 0; + // varargs array length + if (next.opcode >= CodeConstants.opc_bipush && next.opcode <= CodeConstants.opc_sipush) { + varargsCount = next.operand(0); + varargs = true; + + // varargs array creation + if (!iterator.hasNext()) + return false; + next = iterator.next(); + if (next.opcode < CodeConstants.opc_newarray || next.opcode > CodeConstants.opc_anewarray) + return false; + for (int j = 0; j < varargsCount; j++) { + + // duplicate varargs array + if (!iterator.hasNext()) + return false; + next = iterator.next(); + if (next.opcode != CodeConstants.opc_dup) + return false; + + // varargs array index + if (!iterator.hasNext()) + return false; + next = iterator.next(); + if (next.opcode < CodeConstants.opc_bipush || next.opcode > CodeConstants.opc_sipush + || next.operand(0) != j) + return false; + + // load argument for varargs array + if (!iterator.hasNext()) + return false; + next = iterator.next(); + if (next.opcode < CodeConstants.opc_iload || next.opcode > CodeConstants.opc_aload + || next.operand(0) != argument + j) + return false; + + // store argument in varargs array + if (!iterator.hasNext()) + return false; + next = iterator.next(); + if (next.opcode < CodeConstants.opc_iastore || next.opcode > CodeConstants.opc_sastore) + return false; + } + + if (!iterator.hasNext()) + return false; + next = iterator.next(); + } + // invoke method + if (next.opcode < CodeConstants.opc_invokevirtual || next.opcode > CodeConstants.opc_invokeinterface) + return false; + + Instruction invoke = next; + + if (!iterator.hasNext()) + return false; + next = iterator.next(); + + if (next.opcode == CodeConstants.opc_pop) { + if (!iterator.hasNext()) + return false; + next = iterator.next(); + } + + // return + if (next.opcode < CodeConstants.opc_ireturn || next.opcode > CodeConstants.opc_return) + return false; + + if (iterator.hasNext()) + return false; + + LinkConstant link = struct.getPool().getLinkConstant(invoke.operand(0)); + boolean instance = invoke.opcode != CodeConstants.opc_invokestatic + && !link.elementname.equals(CodeConstants.INIT_NAME); + + String newClass = link.classname; + String newMethod = link.elementname; + String newDescriptor = link.descriptor; + String newKey = InterpreterUtil.makeUniqueKey(newMethod, newDescriptor); + + StructClass newStructClass = DecompilerContext.getStructContext().getClass(newClass); + if (newStructClass == null) + return false; + StructMethod newStructMethod = newStructClass.getMethodRecursive(newMethod, newDescriptor); + if (newStructMethod == null) + return false; + + if (newStructMethod.hasModifier(CodeConstants.ACC_SYNTHETIC)) + return false; + + ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(newClass); + if (newNode != null && newNode.type == ClassesProcessor.ClassNode.Type.ANONYMOUS) + return false; + + if (method.methodDescriptor().params.length - (varargs ? varargsCount - 1 : 0) != newStructMethod.methodDescriptor().params.length + (instance ? 1 : 0)) + return false; + if (newExprent.getConstructor().getLstParameters().size() > (instance ? 1 : 0)) + return false; + info.content_class_name = newClass; + info.content_method_name = newMethod; + info.content_method_descriptor = newDescriptor; + info.content_method_invocation_type = switch (invoke.opcode) { + case CodeConstants.opc_invokevirtual -> CodeConstants.CONSTANT_MethodHandle_REF_invokeVirtual; + case CodeConstants.opc_invokespecial -> instance ? CodeConstants.CONSTANT_MethodHandle_REF_newInvokeSpecial : CodeConstants.CONSTANT_MethodHandle_REF_invokeSpecial; + case CodeConstants.opc_invokestatic -> CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic; + case CodeConstants.opc_invokeinterface -> CodeConstants.CONSTANT_MethodHandle_REF_invokeInterface; + default -> -1; + }; + info.content_method_key = newKey; + info.is_method_reference = true; + info.is_content_method_static = invoke.opcode == CodeConstants.opc_invokestatic; + newExprent.setMethodReference(true); + InvocationExprent constructor = newExprent.getConstructor(); + if (instance && constructor.getLstParameters().size() == 1) { + constructor.setInstance(constructor.getLstParameters().get(0)); + } + + if (i > 0) { + Exprent instanceExp = newExprent.getConstructor().getInstance(); + Exprent previous = stat.getExprents().get(i - 1); + if (instanceExp instanceof VarExprent varExp + && previous instanceof AssignmentExprent assignment + && varExp.equalsVersions(assignment.getLeft()) + && !varExp.isVarReferenced(stat.getTopParent(), (VarExprent) assignment.getLeft())) { + newExprent.getConstructor().setInstance(assignment.getRight()); + newExprent.getConstructor().getLstParameters().set(0, assignment.getRight()); + stat.getExprents().remove(i - 1); + } + } + return true; + } + + private static boolean removeInstanceAssignment(Statement stat, int i, Exprent exp) { + if (exp instanceof NewExprent newExp + && newExp.isLambda() + && newExp.isMethodReference() + && i >= 2 + && newExp.getConstructor().getInstance() instanceof VarExprent stackVar + && stackVar.isStack()) { + Exprent nonNull = stat.getExprents().get(i - 1); + Exprent assign = stat.getExprents().get(i - 2); + if (nonNull instanceof InvocationExprent inv + && inv.getClassname().equals("java/util/Objects") + && inv.getName().equals("requireNonNull") + && inv.getStringDescriptor().equals("(Ljava/lang/Object;)Ljava/lang/Object;") + && stackVar.equalsVersions(inv.getLstParameters().get(0)) + && assign instanceof AssignmentExprent stackAssign + && stackVar.equalsVersions(stackAssign.getLeft())) { + newExp.getConstructor().setInstance(stackAssign.getRight()); + newExp.getConstructor().getLstParameters().set(0, stackAssign.getRight()); + stat.getExprents().remove(i - 1); + stat.getExprents().remove(i - 2); + return true; + } + } + return false; + } +} diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java index 1af9d340bc..41530d6ad5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java @@ -851,6 +851,10 @@ public boolean isMethodReference() { return methodReference; } + public void setMethodReference(boolean methodReference) { + this.methodReference = methodReference; + } + public String getLambdaMethodKey() { ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.value); if (node != null && constructor != null) { diff --git a/testData/results/pkg/TestGenericsQualified.dec b/testData/results/pkg/TestGenericsQualified.dec index e4c2b7e981..ebb726ab8c 100644 --- a/testData/results/pkg/TestGenericsQualified.dec +++ b/testData/results/pkg/TestGenericsQualified.dec @@ -6,11 +6,11 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; public class TestGenericsQualified { - public Comparator field = Comparator.comparing(s -> s.length()).thenComparing(i -> i.toString());// 10 + public Comparator field = Comparator.comparing(s -> s.length()).thenComparing(String::toString);// 10 public CompletableFuture field2 = CompletableFuture.supplyAsync(() -> "").thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "2"));// 11 public Optional field3 = Optional.of("").map(s -> s + "3");// 12 - public Stream field4 = Stream.of("1", "2").sorted(Comparator.comparing(s -> s.length()).thenComparing(i -> i.toString()));// 13 14 - public Comparator field5 = Comparator.comparing(String::length).thenComparing(i -> i.toString());// 15 + public Stream field4 = Stream.of("1", "2").sorted(Comparator.comparing(s -> s.length()).thenComparing(String::toString));// 13 14 + public Comparator field5 = Comparator.comparing(String::length).thenComparing(String::toString);// 15 public Comparator field6 = Comparator.comparing(TestGenericsQualified::get).reversed();// 16 public int get() { @@ -22,7 +22,7 @@ public class TestGenericsQualified { } public Comparator method() { - return Comparator.comparing(s -> s.length()).thenComparing(i -> i.toString());// 27 + return Comparator.comparing(s -> s.length()).thenComparing(String::toString);// 27 } } @@ -99,14 +99,6 @@ class 'pkg/TestGenericsQualified' { 7 8 } - method 'lambda$new$1 (Ljava/lang/String;)Ljava/lang/String;' { - 0 8 - 1 8 - 2 8 - 3 8 - 4 8 - } - method 'lambda$new$2 ()Ljava/lang/String;' { 0 9 1 9 @@ -151,22 +143,6 @@ class 'pkg/TestGenericsQualified' { 7 11 } - method 'lambda$new$7 (Ljava/lang/String;)Ljava/lang/String;' { - 0 11 - 1 11 - 2 11 - 3 11 - 4 11 - } - - method 'lambda$new$8 (Ljava/lang/String;)Ljava/lang/String;' { - 0 12 - 1 12 - 2 12 - 3 12 - 4 12 - } - method 'get ()I' { 0 16 1 16 @@ -199,14 +175,6 @@ class 'pkg/TestGenericsQualified' { 6 24 7 24 } - - method 'lambda$method$10 (Ljava/lang/String;)Ljava/lang/String;' { - 0 24 - 1 24 - 2 24 - 3 24 - 4 24 - } } Lines mapping: diff --git a/testData/results/pkg/TestLocalRecord.dec b/testData/results/pkg/TestLocalRecord.dec index e3debbc15c..4f70fa19ba 100644 --- a/testData/results/pkg/TestLocalRecord.dec +++ b/testData/results/pkg/TestLocalRecord.dec @@ -34,21 +34,21 @@ public class TestLocalRecord { record R() { } - Supplier constr = () -> new R();// 29 + Supplier constr = R::new;// 29 }// 30 public Supplier> test5() { record R() { } - return () -> () -> new R();// 34 + return () -> R::new;// 34 } public Supplier test6() { record R() { } - return () -> new R();// 39 + return R::new;// 39 } } @@ -119,10 +119,6 @@ class 'pkg/TestLocalRecord' { 6 37 } - method 'lambda$test4$0 ()Lpkg/TestLocalRecord$3R;' { - 7 36 - } - method 'test5 ()Ljava/util/function/Supplier;' { 5 43 } @@ -131,17 +127,9 @@ class 'pkg/TestLocalRecord' { 5 43 } - method 'lambda$test5$1 ()Ljava/lang/Object;' { - 7 43 - } - method 'test6 ()Ljava/util/function/Supplier;' { 5 50 } - - method 'lambda$test6$3 ()Ljava/lang/Object;' { - 7 50 - } } class 'pkg/TestLocalRecord$2R' { diff --git a/testData/results/pkg/TestMethodReferenceJ21.dec b/testData/results/pkg/TestMethodReferenceJ21.dec index eaa9260e19..cd8833ba50 100644 --- a/testData/results/pkg/TestMethodReferenceJ21.dec +++ b/testData/results/pkg/TestMethodReferenceJ21.dec @@ -5,7 +5,7 @@ import java.util.function.Consumer; public class TestMethodReferenceJ21 extends RefsExt { private void test() { - this.consume(x$0 -> this.accept(x$0));// 9 + this.consume(this::accept);// 9 }// 10 private void test_() { @@ -17,11 +17,11 @@ public class TestMethodReferenceJ21 extends RefsExt { }// 18 private void test3(TestMethodReferenceJ21 ref) { - this.consume(x$0 -> ref.accept(x$0));// 21 + this.consume(ref::accept);// 21 }// 22 private void test3_(TestMethodReferenceJ21 ref) { - this.consume(s -> ref.accept(s));// 25 + this.consume(ref::accept);// 25 }// 26 private void test4(TestMethodReferenceJ21 ref) { @@ -33,7 +33,7 @@ public class TestMethodReferenceJ21 extends RefsExt { }// 34 private void test6() { - this.consume(x$0 -> rec$.accept(x$0));// 37 + this.consume(this.ref()::accept);// 37 }// 38 private void test6_() { @@ -55,21 +55,13 @@ public class TestMethodReferenceJ21 extends RefsExt { class 'pkg/TestMethodReferenceJ21' { method 'test ()V' { 0 7 + 1 7 7 7 8 7 9 7 a 8 } - method 'lambda$test$0 (Lpkg/TestMethodReferenceJ21;Ljava/lang/String;)V' { - 0 7 - 1 7 - 2 7 - 3 7 - 4 7 - 5 7 - } - method 'test_ ()V' { 0 11 7 11 @@ -98,38 +90,22 @@ class 'pkg/TestMethodReferenceJ21' { method 'test3 (Lpkg/TestMethodReferenceJ21;)V' { 0 19 + 1 19 c 19 d 19 e 19 f 20 } - method 'lambda$test3$2 (Lpkg/TestMethodReferenceJ21;Ljava/lang/String;)V' { - 0 19 - 1 19 - 2 19 - 3 19 - 4 19 - 5 19 - } - method 'test3_ (Lpkg/TestMethodReferenceJ21;)V' { 0 23 + 1 23 7 23 8 23 9 23 a 24 } - method 'lambda$test3_$3 (Lpkg/TestMethodReferenceJ21;Ljava/lang/String;)V' { - 0 23 - 1 23 - 2 23 - 3 23 - 4 23 - 5 23 - } - method 'test4 (Lpkg/TestMethodReferenceJ21;)V' { 0 27 1 27 @@ -149,20 +125,15 @@ class 'pkg/TestMethodReferenceJ21' { } method 'test6 ()V' { - 0 35 - f 35 - 10 35 - 11 35 - 12 36 - } - - method 'lambda$test6$4 (Lpkg/TestMethodReferenceJ21;Ljava/lang/String;)V' { 0 35 1 35 2 35 3 35 4 35 - 5 35 + f 35 + 10 35 + 11 35 + 12 36 } method 'test6_ ()V' { @@ -229,4 +200,4 @@ Lines mapping: 45 <-> 44 46 <-> 45 49 <-> 48 -54 <-> 52 \ No newline at end of file +54 <-> 52 diff --git a/testData/results/pkg/TestMethodReferenceJ25.dec b/testData/results/pkg/TestMethodReferenceJ25.dec index fdcef5acd8..b51383fa2e 100644 --- a/testData/results/pkg/TestMethodReferenceJ25.dec +++ b/testData/results/pkg/TestMethodReferenceJ25.dec @@ -1,13 +1,11 @@ package pkg; import ext.RefsExt; -import java.util.Objects; import java.util.function.Consumer; public class TestMethodReferenceJ25 extends RefsExt { private void test() { - TestMethodReferenceJ25 var1 = this; - this.consume(x$0 -> var1.accept(x$0));// 9 + this.consume(this::accept);// 9 }// 10 private void test_() { @@ -19,13 +17,11 @@ public class TestMethodReferenceJ25 extends RefsExt { }// 18 private void test3(TestMethodReferenceJ25 ref) { - Objects.requireNonNull(ref); - TestMethodReferenceJ25 var2 = ref; - this.consume(x$0 -> var2.accept(x$0));// 21 + this.consume(ref::accept);// 21 }// 22 private void test3_(TestMethodReferenceJ25 ref) { - this.consume(s -> ref.accept(s));// 25 + this.consume(ref::accept);// 25 }// 26 private void test4(TestMethodReferenceJ25 ref) { @@ -37,10 +33,7 @@ public class TestMethodReferenceJ25 extends RefsExt { }// 34 private void test6() { - TestMethodReferenceJ25 var10001 = this.ref(); - Objects.requireNonNull(var10001); - TestMethodReferenceJ25 var1 = var10001; - this.consume(x$0 -> var1.accept(x$0));// 37 + this.consume(this.ref()::accept);// 37 }// 38 private void test6_() { @@ -61,90 +54,68 @@ public class TestMethodReferenceJ25 extends RefsExt { class 'pkg/TestMethodReferenceJ25' { method 'test ()V' { - 0 9 - 1 8 - 2 8 - 9 9 - a 9 - b 9 - c 10 - } - - method 'lambda$test$0 (Lpkg/TestMethodReferenceJ25;Ljava/lang/String;)V' { - 0 9 - 1 9 - 2 9 - 3 9 - 4 9 - 5 9 + 0 7 + 1 7 + 9 7 + a 7 + b 7 + c 8 } method 'test_ ()V' { - 0 13 - 7 13 - 8 13 - 9 13 - a 14 + 0 11 + 7 11 + 8 11 + 9 11 + a 12 } method 'lambda$test_$0 (Ljava/lang/String;)V' { - 0 13 - 1 13 - 2 13 - 3 13 - 4 13 - 5 13 + 0 11 + 1 11 + 2 11 + 3 11 + 4 11 + 5 11 } method 'test2 ()V' { - 0 17 - 1 17 - 7 17 - 8 17 - 9 17 - a 18 + 0 15 + 1 15 + 7 15 + 8 15 + 9 15 + a 16 } method 'test3 (Lpkg/TestMethodReferenceJ25;)V' { - 0 23 - 1 21 - 3 21 - 4 21 - 5 21 - 7 22 - e 23 - f 23 - 10 23 - 11 24 + 0 19 + 1 19 + e 19 + f 19 + 10 19 + 11 20 } - method 'lambda$test3$0 (Lpkg/TestMethodReferenceJ25;Ljava/lang/String;)V' { + method 'test3_ (Lpkg/TestMethodReferenceJ25;)V' { 0 23 1 23 - 2 23 - 3 23 - 4 23 - 5 23 + 7 23 + 8 23 + 9 23 + a 24 } - method 'test3_ (Lpkg/TestMethodReferenceJ25;)V' { - 0 27 - 7 27 - 8 27 - 9 27 - a 28 - } - - method 'lambda$test3_$0 (Lpkg/TestMethodReferenceJ25;Ljava/lang/String;)V' { + method 'test4 (Lpkg/TestMethodReferenceJ25;)V' { 0 27 1 27 - 2 27 - 3 27 - 4 27 - 5 27 + c 27 + d 27 + e 27 + f 28 } - method 'test4 (Lpkg/TestMethodReferenceJ25;)V' { + method 'test5 (Lext/RefsExt;)V' { 0 31 1 31 c 31 @@ -153,102 +124,80 @@ class 'pkg/TestMethodReferenceJ25' { f 32 } - method 'test5 (Lext/RefsExt;)V' { + method 'test6 ()V' { 0 35 1 35 - c 35 - d 35 - e 35 - f 36 - } - - method 'test6 ()V' { - 0 42 - 1 39 - 2 39 - 3 39 - 4 39 - 6 40 - 7 40 - 8 40 - a 41 - 11 42 - 12 42 - 13 42 - 14 43 - } - - method 'lambda$test6$0 (Lpkg/TestMethodReferenceJ25;Ljava/lang/String;)V' { - 0 42 - 1 42 - 2 42 - 3 42 - 4 42 - 5 42 + 2 35 + 3 35 + 4 35 + 11 35 + 12 35 + 13 35 + 14 36 } method 'test6_ ()V' { - 0 46 - 7 46 - 8 46 - 9 46 - a 47 + 0 39 + 7 39 + 8 39 + 9 39 + a 40 } method 'lambda$test6_$0 (Ljava/lang/String;)V' { - 0 46 - 1 46 - 2 46 - 3 46 - 4 46 - 5 46 - 6 46 - 7 46 - 8 46 + 0 39 + 1 39 + 2 39 + 3 39 + 4 39 + 5 39 + 6 39 + 7 39 + 8 39 } method 'test7 ()V' { - 0 50 - 1 50 - 2 50 - 3 50 - 4 50 - f 50 - 10 50 - 11 50 - 12 51 + 0 43 + 1 43 + 2 43 + 3 43 + 4 43 + f 43 + 10 43 + 11 43 + 12 44 } method 'ref ()Lpkg/TestMethodReferenceJ25;' { - 0 54 - 1 54 + 0 47 + 1 47 } method 'consume (Ljava/util/function/Consumer;)V' { - 0 58 + 0 51 } } Lines mapping: -9 <-> 10 -10 <-> 11 -13 <-> 14 -14 <-> 15 -17 <-> 18 -18 <-> 19 -21 <-> 24 -22 <-> 25 -25 <-> 28 -26 <-> 29 -29 <-> 32 -30 <-> 33 -33 <-> 36 -34 <-> 37 -37 <-> 43 -38 <-> 44 -41 <-> 47 -42 <-> 48 -45 <-> 51 -46 <-> 52 -49 <-> 55 -54 <-> 59 \ No newline at end of file +9 <-> 8 +10 <-> 9 +13 <-> 12 +14 <-> 13 +17 <-> 16 +18 <-> 17 +21 <-> 20 +22 <-> 21 +25 <-> 24 +26 <-> 25 +29 <-> 28 +30 <-> 29 +33 <-> 32 +34 <-> 33 +37 <-> 36 +38 <-> 37 +41 <-> 40 +42 <-> 41 +45 <-> 44 +46 <-> 45 +49 <-> 48 +54 <-> 52