1 /*
   2  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * @test
  28  * @bug 8019486 8026861 8027142
  29  * @summary javac, generates erroneous LVT for a test case with lambda code
  30  * @library /tools/lib
  31  * @modules jdk.jdeps/com.sun.tools.classfile
  32  *          jdk.compiler/com.sun.tools.javac.api
  33  *          jdk.compiler/com.sun.tools.javac.file
  34  *          jdk.compiler/com.sun.tools.javac.main
  35  *          jdk.compiler/com.sun.tools.javac.util
  36  * @build ToolBox
  37  * @run main WrongLNTForLambdaTest
  38  */
  39 
  40 import java.io.File;
  41 import java.nio.file.Paths;
  42 
  43 import com.sun.tools.classfile.ClassFile;
  44 import com.sun.tools.classfile.Code_attribute;
  45 import com.sun.tools.classfile.LineNumberTable_attribute;
  46 import com.sun.tools.classfile.Method;
  47 import com.sun.tools.javac.util.Assert;
  48 
  49 public class WrongLNTForLambdaTest {
  50 
  51     static final String testSource =
  52     /* 01 */        "import java.util.List;\n" +
  53     /* 02 */        "import java.util.Arrays;\n" +
  54     /* 03 */        "import java.util.stream.Collectors;\n" +
  55     /* 04 */        "\n" +
  56     /* 05 */        "public class Foo {\n" +
  57     /* 06 */        "    void bar(int value) {\n" +
  58     /* 07 */        "        final List<Integer> numbers = Arrays.asList(1, 2, 3);\n" +
  59     /* 08 */        "        final List<Integer> numbersPlusOne = \n" +
  60     /* 09 */        "             numbers.stream().map(number -> number / 1).collect(Collectors.toList());\n" +
  61     /* 10 */        "    }\n" +
  62     /* 11 */        "    void variablesInLambdas(int value) {\n" +
  63     /* 12 */        "        Runnable r1 = () -> {\n" +
  64     /* 13 */        "            int i  = value;\n" +
  65     /* 14 */        "            class FooBar<T extends CharSequence> {\n" +
  66     /* 15 */        "                public void run() {\n" +
  67     /* 16 */        "                    T t = null;\n" +
  68     /* 17 */        "                }\n" +
  69     /* 18 */        "            }\n" +
  70     /* 19 */        "        };\n" +
  71     /* 20 */        "        Runnable r2 = () -> System.err.println(1);\n" +
  72     /* 21 */        "        Runnable r3 = (Runnable & java.io.Serializable) this::foo;\n" +
  73     /* 22 */        "        Runnable r4 = super :: notify;\n" +
  74     /* 23 */        "    }\n" +
  75     /* 24 */        "    private void foo() {}\n" +
  76     /* 25 */        "    void assignLambda() {\n" +
  77     /* 26 */        "        Runnable r = () -> { };\n" +
  78     /* 27 */        "    }\n" +
  79     /* 28 */        "    void callLambda(int i, Runnable r) {\n" +
  80     /* 29 */        "        callLambda(0,\n" +
  81     /* 30 */        "                   () -> { });\n" +
  82     /* 31 */        "    }\n" +
  83     /* 32 */        "}";
  84 
  85     static final int[][] simpleLambdaExpectedLNT = {
  86     //  {line-number, start-pc},
  87         {9,           0},       //number -> number / 1
  88     };
  89 
  90     static final int[][] lambdaWithVarsExpectedLNT = {
  91     //  {line-number, start-pc},
  92         {13,           0},       //number -> number / 1
  93         {19,           2},       //number -> number / 1
  94     };
  95 
  96     static final int[][] insideLambdaWithVarsExpectedLNT = {
  97     //  {line-number, start-pc},
  98         {16,           0},       //number -> number / 1
  99         {17,           2},       //number -> number / 1
 100     };
 101 
 102     static final int[][] lambdaVoid2VoidExpectedLNT = {
 103     //  {line-number, start-pc},
 104         {20,           0},       //number -> number / 1
 105     };
 106 
 107     static final int[][] deserializeExpectedLNT = {
 108     //  {line-number, start-pc},
 109         {05,           0},       //number -> number / 1
 110     };
 111 
 112     static final int[][] lambdaBridgeExpectedLNT = {
 113     //  {line-number, start-pc},
 114         {22,           0},       //number -> number / 1
 115     };
 116 
 117     static final int[][] assignmentExpectedLNT = {
 118     //  {line-number, start-pc},
 119         {26,           0},       //number -> number / 1
 120         {27,           6},       //number -> number / 1
 121     };
 122 
 123     static final int[][] callExpectedLNT = {
 124     //  {line-number, start-pc},
 125         {29,           0},       //number -> number / 1
 126         {31,           10},       //number -> number / 1
 127     };
 128 
 129     public static void main(String[] args) throws Exception {
 130         new WrongLNTForLambdaTest().run();
 131     }
 132 
 133     ToolBox tb = new ToolBox();
 134 
 135     void run() throws Exception {
 136         compileTestClass();
 137         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 138                 "Foo.class").toUri()), "lambda$bar$0", simpleLambdaExpectedLNT);
 139         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 140                 "Foo.class").toUri()), "lambda$variablesInLambdas$1", lambdaWithVarsExpectedLNT);
 141         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 142                 "Foo$1FooBar.class").toUri()), "run", insideLambdaWithVarsExpectedLNT);
 143         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 144                 "Foo.class").toUri()), "lambda$variablesInLambdas$2", lambdaVoid2VoidExpectedLNT);
 145         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 146                 "Foo.class").toUri()), "$deserializeLambda$", deserializeExpectedLNT);
 147         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 148                 "Foo.class").toUri()), "lambda$variablesInLambdas$3", lambdaBridgeExpectedLNT);
 149         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 150                 "Foo.class").toUri()), "assignLambda", assignmentExpectedLNT);
 151         checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
 152                 "Foo.class").toUri()), "callLambda", callExpectedLNT);
 153     }
 154 
 155     void compileTestClass() throws Exception {
 156         tb.new JavacTask()
 157                 .sources(testSource)
 158                 .run();
 159     }
 160 
 161     void checkClassFile(final File cfile, String methodToFind, int[][] expectedLNT) throws Exception {
 162         ClassFile classFile = ClassFile.read(cfile);
 163         boolean methodFound = false;
 164         for (Method method : classFile.methods) {
 165             if (method.getName(classFile.constant_pool).equals(methodToFind)) {
 166                 methodFound = true;
 167                 Code_attribute code = (Code_attribute) method.attributes.get("Code");
 168                 LineNumberTable_attribute lnt =
 169                         (LineNumberTable_attribute) code.attributes.get("LineNumberTable");
 170                 Assert.check(lnt.line_number_table_length == expectedLNT.length,
 171                         "The LineNumberTable found has a length different to the expected one");
 172                 int i = 0;
 173                 for (LineNumberTable_attribute.Entry entry: lnt.line_number_table) {
 174                     Assert.check(entry.line_number == expectedLNT[i][0] &&
 175                             entry.start_pc == expectedLNT[i][1],
 176                             "LNT entry at pos " + i + " differ from expected." +
 177                             "Found " + entry.line_number + ":" + entry.start_pc +
 178                             ". Expected " + expectedLNT[i][0] + ":" + expectedLNT[i][1]);
 179                     i++;
 180                 }
 181             }
 182         }
 183         Assert.check(methodFound, "The seek method was not found");
 184     }
 185 
 186     void error(String msg) {
 187         throw new AssertionError(msg);
 188     }
 189 
 190 }