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. 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 */ 23 24 /* 25 * @test 26 * @bug 8025998 8026749 8054220 8058227 27 * @summary Missing LV table in lambda bodies 28 * @modules jdk.jdeps/com.sun.tools.classfile 29 * @compile -g LocalVariableTable.java 30 * @run main LocalVariableTable 31 */ 32 33 import java.io.*; 34 import java.lang.annotation.*; 35 import java.util.*; 36 import com.sun.tools.classfile.*; 37 38 /* 39 * The test checks that a LocalVariableTable attribute is generated for the 40 * method bodies representing lambda expressions, and checks that the expected 41 * set of entries is found in the attribute. 42 * 43 * Since the bug was about missing entries in the LVT, not malformed entries, 44 * the test is not intended to be a detailed test of the contents of each 45 * LocalVariableTable entry: it is assumed that if a entry is present, it 46 * will have the correct contents. 47 * 48 * The test looks for test cases represented by nested classes whose 49 * name begins with "Lambda". Each such class contains a lambda expression 50 * that will mapped into a lambda method, and because the test is compiled 51 * with -g, these methods should have a LocalVariableTable. The set of 52 * expected names in the LVT is provided in an annotation on the class for 53 * the test case. 54 */ 55 public class LocalVariableTable { 56 public static void main(String... args) throws Exception { 57 new LocalVariableTable().run(); 58 } 59 60 void run() throws Exception { 61 // the declared classes are returned in an unspecified order, 62 // so for neatness, sort them by name before processing them 63 Class<?>[] classes = getClass().getDeclaredClasses(); 64 Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName())); 65 66 for (Class<?> c : classes) { 67 if (c.getSimpleName().startsWith("Lambda")) 68 check(c); 69 } 70 if (errors > 0) 71 throw new Exception(errors + " errors found"); 72 } 73 74 /** Check an individual test case. */ 75 void check(Class<?> c) throws Exception { 76 System.err.println("Checking " + c.getSimpleName()); 77 78 Expect expect = c.getAnnotation(Expect.class); 79 if (expect == null) { 80 error("@Expect not found for class " + c.getSimpleName()); 81 return; 82 } 83 84 ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream()); 85 Method m = getLambdaMethod(cf); 86 if (m == null) { 87 error("lambda method not found"); 88 return; 89 } 90 91 Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code); 92 if (code == null) { 93 error("Code attribute not found"); 94 return; 95 } 96 97 LocalVariableTable_attribute lvt = 98 (LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable); 99 if (lvt == null) { 100 error("LocalVariableTable attribute not found"); 101 return; 102 } 103 104 Set<String> foundNames = new LinkedHashSet<>(); 105 for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) { 106 foundNames.add(cf.constant_pool.getUTF8Value(e.name_index)); 107 } 108 109 Set<String> expectNames = new LinkedHashSet<>(Arrays.asList(expect.value())); 110 if (!foundNames.equals(expectNames)) { 111 Set<String> foundOnly = new LinkedHashSet<>(foundNames); 112 foundOnly.removeAll(expectNames); 113 for (String s: foundOnly) 114 error("Unexpected name found: " + s); 115 Set<String> expectOnly = new LinkedHashSet<>(expectNames); 116 expectOnly.removeAll(foundNames); 117 for (String s: expectOnly) 118 error("Expected name not found: " + s); 119 } 120 } 121 122 /** Get a method whose name begins "lambda$...". */ 123 Method getLambdaMethod(ClassFile cf) throws ConstantPoolException { 124 for (Method m: cf.methods) { 125 if (m.getName(cf.constant_pool).startsWith("lambda$")) 126 return m; 127 } 128 return null; 129 } 130 131 /** Report an error. */ 132 void error(String msg) { 133 System.err.println("Error: " + msg); 134 errors++; 135 } 136 137 int errors; 138 139 /** 140 * Annotation used to provide the set of names expected in the LVT attribute. 141 */ 142 @Retention(RetentionPolicy.RUNTIME) 143 @interface Expect { 144 String[] value(); 145 } 146 147 /** Functional interface with nullary method. */ 148 interface Run0 { 149 public void run(); 150 } 151 152 /** Functional interface with 1-ary method. */ 153 interface Run1 { 154 public void run(int a0); 155 } 156 157 /** Functional interface with 2-ary method. */ 158 interface Run2 { 159 public void run(int a0, int a1); 160 } 161 162 /* 163 * ---------- Test cases --------------------------------------------------- 164 */ 165 166 @Expect({ "x" }) 167 static class Lambda_Args0_Local1 { 168 Run0 r = () -> { int x = 0; }; 169 } 170 171 @Expect({ "x", "this" }) 172 static class Lambda_Args0_Local1_this { 173 int v; 174 Run0 r = () -> { int x = v; }; 175 } 176 177 @Expect({ "a" }) 178 static class Lambda_Args1_Local0 { 179 Run1 r = (a) -> { }; 180 } 181 182 @Expect({ "a", "x" }) 183 static class Lambda_Args1_Local1 { 184 Run1 r = (a) -> { int x = a; }; 185 } 186 187 @Expect({ "a", "x", "v" }) 188 static class Lambda_Args1_Local1_Captured1 { 189 void m() { 190 int v = 0; 191 Run1 r = (a) -> { int x = a + v; }; 192 } 193 } 194 195 @Expect({ "a1", "a2", "x1", "x2", "this", "v1", "v2" }) 196 static class Lambda_Args2_Local2_Captured2_this { 197 int v; 198 void m() { 199 int v1 = 0; 200 int v2 = 0; 201 Run2 r = (a1, a2) -> { 202 int x1 = a1 + v1 + v; 203 int x2 = a2 + v2 + v; 204 }; 205 } 206 } 207 208 @Expect({ "e", "c" }) 209 static class Lambda_Try_Catch { 210 private static Runnable asUncheckedRunnable(Closeable c) { 211 return () -> { 212 try { 213 c.close(); 214 } catch (IOException e) { 215 throw new UncheckedIOException(e); 216 } 217 }; 218 } 219 } 220 } 221