test/tools/javac/lambda/LocalVariableTable.java

changeset 2135
d7e155f874a7
parent 2114
09a414673570
equal deleted inserted replaced
2134:b0c086cd4520 2135:d7e155f874a7
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 */
23
24 /*
25 * @test
26 * @bug 8025998
27 * @summary Missing LV table in lambda bodies
28 * @compile -g LocalVariableTable.java
29 * @run main LocalVariableTable
30 */
31
32 import java.lang.annotation.*;
33 import java.util.*;
34 import com.sun.tools.classfile.*;
35
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 }
57
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()));
63
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 }
71
72 /** Check an individual test case. */
73 void check(Class<?> c) throws Exception {
74 System.err.println("Checking " + c.getSimpleName());
75
76 Expect expect = c.getAnnotation(Expect.class);
77 if (expect == null) {
78 error("@Expect not found for class " + c.getSimpleName());
79 return;
80 }
81
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 }
88
89 Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code);
90 if (code == null) {
91 error("Code attribute not found");
92 return;
93 }
94
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 }
101
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 }
106
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 }
119
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 }
128
129 /** Report an error. */
130 void error(String msg) {
131 System.err.println("Error: " + msg);
132 errors++;
133 }
134
135 int errors;
136
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 }
144
145 /** Functional interface with nullary method. */
146 interface Run0 {
147 public void run();
148 }
149
150 /** Functional interface with 1-ary method. */
151 interface Run1 {
152 public void run(int a0);
153 }
154
155 /** Functional interface with 2-ary method. */
156 interface Run2 {
157 public void run(int a0, int a1);
158 }
159
160 /*
161 * ---------- Test cases ---------------------------------------------------
162 */
163
164 @Expect({ "x" })
165 static class Lambda_Args0_Local1 {
166 Run0 r = () -> { int x = 0; };
167 }
168
169 @Expect({ "x", "this" })
170 static class Lambda_Args0_Local1_this {
171 int v;
172 Run0 r = () -> { int x = v; };
173 }
174
175 @Expect({ "a" })
176 static class Lambda_Args1_Local0 {
177 Run1 r = (a) -> { };
178 }
179
180 @Expect({ "a", "x" })
181 static class Lambda_Args1_Local1 {
182 Run1 r = (a) -> { int x = a; };
183 }
184
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 }
192
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 }
206

mercurial