test/tools/javac/lambda/LocalVariableTable.java

Mon, 14 Oct 2013 23:07:43 -0700

author
jjg
date
Mon, 14 Oct 2013 23:07:43 -0700
changeset 2114
09a414673570
permissions
-rw-r--r--

8025998: Missing LV table in lambda bodies
Reviewed-by: vromero

     1 /*
     2  * Copyright (c) 2013, 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.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    22  */
    24 /*
    25  * @test
    26  * @bug 8025998
    27  * @summary Missing LV table in lambda bodies
    28  * @compile -g LocalVariableTable.java
    29  * @run main LocalVariableTable
    30  */
    32 import java.lang.annotation.*;
    33 import java.util.*;
    34 import com.sun.tools.classfile.*;
    36 /*
    37  * The test checks that a LocalVariableTable attribute is generated for the
    38  * method bodies representing lambda expressions, and checks that the expected
    39  * set of entries is found in the attribute.
    40  *
    41  * Since the bug was about missing entries in the LVT, not malformed entries,
    42  * the test is not intended to be a detailed test of the contents of each
    43  * LocalVariableTable entry: it is assumed that if a entry is present, it
    44  * will have the correct contents.
    45  *
    46  * The test looks for test cases represented by nested classes whose
    47  * name begins with "Lambda".  Each such class contains a lambda expression
    48  * that will mapped into a lambda method, and because the test is compiled
    49  * with -g, these methods should have a LocalVariableTable.  The set of
    50  * expected names in the LVT is provided in an annotation on the class for
    51  * the test case.
    52  */
    53 public class LocalVariableTable {
    54     public static void main(String... args) throws Exception {
    55         new LocalVariableTable().run();
    56     }
    58     void run() throws Exception {
    59         // the declared classes are returned in an unspecified order,
    60         // so for neatness, sort them by name before processing them
    61         Class<?>[] classes = getClass().getDeclaredClasses();
    62         Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName()));
    64         for (Class<?> c : classes) {
    65             if (c.getSimpleName().startsWith("Lambda"))
    66                 check(c);
    67         }
    68         if (errors > 0)
    69             throw new Exception(errors + " errors found");
    70     }
    72     /** Check an individual test case. */
    73     void check(Class<?> c) throws Exception {
    74         System.err.println("Checking " + c.getSimpleName());
    76         Expect expect = c.getAnnotation(Expect.class);
    77         if (expect == null) {
    78             error("@Expect not found for class " + c.getSimpleName());
    79             return;
    80         }
    82         ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream());
    83         Method m = getLambdaMethod(cf);
    84         if (m == null) {
    85             error("lambda method not found");
    86             return;
    87         }
    89         Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code);
    90         if (code == null) {
    91             error("Code attribute not found");
    92             return;
    93         }
    95         LocalVariableTable_attribute lvt =
    96                 (LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable);
    97         if (lvt == null) {
    98             error("LocalVariableTable attribute not found");
    99             return;
   100         }
   102         Set<String> foundNames = new LinkedHashSet<>();
   103         for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) {
   104             foundNames.add(cf.constant_pool.getUTF8Value(e.name_index));
   105         }
   107         Set<String> expectNames = new LinkedHashSet<>(Arrays.asList(expect.value()));
   108         if (!foundNames.equals(expectNames)) {
   109             Set<String> foundOnly = new LinkedHashSet<>(foundNames);
   110             foundOnly.removeAll(expectNames);
   111             for (String s: foundOnly)
   112                 error("Unexpected name found: " + s);
   113             Set<String> expectOnly = new LinkedHashSet<>(expectNames);
   114             expectOnly.removeAll(foundNames);
   115             for (String s: expectOnly)
   116                 error("Expected name not found: " + s);
   117         }
   118     }
   120     /** Get a method whose name begins "lambda$...". */
   121     Method getLambdaMethod(ClassFile cf) throws ConstantPoolException {
   122         for (Method m: cf.methods) {
   123             if (m.getName(cf.constant_pool).startsWith("lambda$"))
   124                 return m;
   125         }
   126         return null;
   127     }
   129     /** Report an error. */
   130     void error(String msg) {
   131         System.err.println("Error: " + msg);
   132         errors++;
   133     }
   135     int errors;
   137     /**
   138      * Annotation used to provide the set of names expected in the LVT attribute.
   139      */
   140     @Retention(RetentionPolicy.RUNTIME)
   141     @interface Expect {
   142         String[] value();
   143     }
   145     /** Functional interface with nullary method. */
   146     interface Run0 {
   147         public void run();
   148     }
   150     /** Functional interface with 1-ary method. */
   151     interface Run1 {
   152         public void run(int a0);
   153     }
   155     /** Functional interface with 2-ary method. */
   156     interface Run2 {
   157         public void run(int a0, int a1);
   158     }
   160     /*
   161      * ---------- Test cases ---------------------------------------------------
   162      */
   164     @Expect({ "x" })
   165     static class Lambda_Args0_Local1 {
   166         Run0 r = () -> { int x = 0; };
   167     }
   169     @Expect({ "x", "this" })
   170     static class Lambda_Args0_Local1_this {
   171         int v;
   172         Run0 r = () -> { int x = v; };
   173     }
   175     @Expect({ "a" })
   176     static class Lambda_Args1_Local0 {
   177         Run1 r = (a) -> { };
   178     }
   180     @Expect({ "a", "x" })
   181     static class Lambda_Args1_Local1 {
   182         Run1 r = (a) -> { int x = a; };
   183     }
   185     @Expect({ "a", "x" })
   186     static class Lambda_Args1_Local1_Captured1 {
   187         void m() {
   188             int v = 0;
   189             Run1 r = (a) -> { int x = a + v; };
   190         }
   191     }
   193     @Expect({ "a1", "a2", "x1", "x2", "this" })
   194     static class Lambda_Args2_Local2_Captured2_this {
   195         int v;
   196         void m() {
   197             int v1 = 0;
   198             int v2 = 0;
   199             Run2 r = (a1, a2) -> {
   200                 int x1 = a1 + v1 + v;
   201                 int x2 = a2 + v2 + v;
   202             };
   203         }
   204     }
   205 }

mercurial