Tue, 10 Sep 2013 13:47:51 +0200
8005222: Fixed bugs should have tests with bugid in @bug tag
Reviewed-by: jfranck, jjg
Contributed-by: Andreas Lundblad <andreas.lundblad@oracle.com>
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 8009649
27 * @summary Lambda back-end should generate invokespecial for method handles referring to private instance methods
28 * @library ../../lib
29 * @build JavacTestingAbstractThreadedTest
30 * @run main/othervm TestLambdaBytecode
31 */
33 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
34 // see JDK-8006746
36 import com.sun.tools.classfile.Attribute;
37 import com.sun.tools.classfile.BootstrapMethods_attribute;
38 import com.sun.tools.classfile.ClassFile;
39 import com.sun.tools.classfile.Code_attribute;
40 import com.sun.tools.classfile.ConstantPool.*;
41 import com.sun.tools.classfile.Instruction;
42 import com.sun.tools.classfile.Method;
44 import com.sun.tools.javac.api.JavacTaskImpl;
47 import java.io.File;
48 import java.net.URI;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Locale;
53 import javax.tools.Diagnostic;
54 import javax.tools.JavaFileObject;
55 import javax.tools.SimpleJavaFileObject;
57 import static com.sun.tools.javac.jvm.ClassFile.*;
59 public class TestLambdaBytecode
60 extends JavacTestingAbstractThreadedTest
61 implements Runnable {
63 enum ClassKind {
64 CLASS("class"),
65 INTERFACE("interface");
67 String classStr;
69 ClassKind(String classStr) {
70 this.classStr = classStr;
71 }
72 }
74 enum AccessKind {
75 PUBLIC("public"),
76 PRIVATE("private");
78 String accessStr;
80 AccessKind(String accessStr) {
81 this.accessStr = accessStr;
82 }
83 }
85 enum StaticKind {
86 STATIC("static"),
87 INSTANCE("");
89 String staticStr;
91 StaticKind(String staticStr) {
92 this.staticStr = staticStr;
93 }
94 }
96 enum DefaultKind {
97 DEFAULT("default"),
98 NO_DEFAULT("");
100 String defaultStr;
102 DefaultKind(String defaultStr) {
103 this.defaultStr = defaultStr;
104 }
105 }
107 enum ExprKind {
108 LAMBDA("Runnable r = ()->{ target(); };");
110 String exprString;
112 ExprKind(String exprString) {
113 this.exprString = exprString;
114 }
115 }
117 static class MethodKind {
118 ClassKind ck;
119 AccessKind ak;
120 StaticKind sk;
121 DefaultKind dk;
123 MethodKind(ClassKind ck, AccessKind ak, StaticKind sk, DefaultKind dk) {
124 this.ck = ck;
125 this.ak = ak;
126 this.sk = sk;
127 this.dk = dk;
128 }
130 boolean inInterface() {
131 return ck == ClassKind.INTERFACE;
132 }
134 boolean isPrivate() {
135 return ak == AccessKind.PRIVATE;
136 }
138 boolean isStatic() {
139 return sk == StaticKind.STATIC;
140 }
142 boolean isDefault() {
143 return dk == DefaultKind.DEFAULT;
144 }
146 boolean isOK() {
147 if (isDefault() && (!inInterface() || isStatic())) {
148 return false;
149 } else if (inInterface() &&
150 ((!isStatic() && !isDefault()) || isPrivate())) {
151 return false;
152 } else {
153 return true;
154 }
155 }
157 String mods() {
158 StringBuilder buf = new StringBuilder();
159 buf.append(ak.accessStr);
160 buf.append(' ');
161 buf.append(sk.staticStr);
162 buf.append(' ');
163 buf.append(dk.defaultStr);
164 return buf.toString();
165 }
166 }
168 public static void main(String... args) throws Exception {
169 for (ClassKind ck : ClassKind.values()) {
170 for (AccessKind ak1 : AccessKind.values()) {
171 for (StaticKind sk1 : StaticKind.values()) {
172 for (DefaultKind dk1 : DefaultKind.values()) {
173 for (AccessKind ak2 : AccessKind.values()) {
174 for (StaticKind sk2 : StaticKind.values()) {
175 for (DefaultKind dk2 : DefaultKind.values()) {
176 for (ExprKind ek : ExprKind.values()) {
177 pool.execute(new TestLambdaBytecode(ck, ak1, ak2, sk1, sk2, dk1, dk2, ek));
178 }
179 }
180 }
181 }
182 }
183 }
184 }
185 }
187 checkAfterExec();
188 }
190 MethodKind mk1, mk2;
191 ExprKind ek;
192 DiagChecker dc;
194 TestLambdaBytecode(ClassKind ck, AccessKind ak1, AccessKind ak2, StaticKind sk1,
195 StaticKind sk2, DefaultKind dk1, DefaultKind dk2, ExprKind ek) {
196 mk1 = new MethodKind(ck, ak1, sk1, dk1);
197 mk2 = new MethodKind(ck, ak2, sk2, dk2);
198 this.ek = ek;
199 dc = new DiagChecker();
200 }
202 public void run() {
203 int id = checkCount.incrementAndGet();
204 JavaSource source = new JavaSource(id);
205 JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc,
206 null, null, Arrays.asList(source));
207 try {
208 ct.generate();
209 } catch (Throwable t) {
210 t.printStackTrace();
211 throw new AssertionError(
212 String.format("Error thrown when compiling following code\n%s",
213 source.source));
214 }
215 if (dc.diagFound) {
216 boolean errorExpected = !mk1.isOK() || !mk2.isOK();
217 errorExpected |= mk1.isStatic() && !mk2.isStatic();
219 if (!errorExpected) {
220 throw new AssertionError(
221 String.format("Diags found when compiling following code\n%s\n\n%s",
222 source.source, dc.printDiags()));
223 }
224 return;
225 }
226 verifyBytecode(id, source);
227 }
229 void verifyBytecode(int id, JavaSource source) {
230 File compiledTest = new File(String.format("Test%d.class", id));
231 try {
232 ClassFile cf = ClassFile.read(compiledTest);
233 Method testMethod = null;
234 for (Method m : cf.methods) {
235 if (m.getName(cf.constant_pool).equals("test")) {
236 testMethod = m;
237 break;
238 }
239 }
240 if (testMethod == null) {
241 throw new Error("Test method not found");
242 }
243 Code_attribute ea =
244 (Code_attribute)testMethod.attributes.get(Attribute.Code);
245 if (testMethod == null) {
246 throw new Error("Code attribute for test() method not found");
247 }
249 int bsmIdx = -1;
251 for (Instruction i : ea.getInstructions()) {
252 if (i.getMnemonic().equals("invokedynamic")) {
253 CONSTANT_InvokeDynamic_info indyInfo =
254 (CONSTANT_InvokeDynamic_info)cf
255 .constant_pool.get(i.getShort(1));
256 bsmIdx = indyInfo.bootstrap_method_attr_index;
257 if (!indyInfo.getNameAndTypeInfo().getType().equals(makeIndyType(id))) {
258 throw new
259 AssertionError("type mismatch for CONSTANT_InvokeDynamic_info " + source.source + "\n" + indyInfo.getNameAndTypeInfo().getType() + "\n" + makeIndyType(id));
260 }
261 }
262 }
263 if (bsmIdx == -1) {
264 throw new Error("Missing invokedynamic in generated code");
265 }
267 BootstrapMethods_attribute bsm_attr =
268 (BootstrapMethods_attribute)cf
269 .getAttribute(Attribute.BootstrapMethods);
270 if (bsm_attr.bootstrap_method_specifiers.length != 1) {
271 throw new Error("Bad number of method specifiers " +
272 "in BootstrapMethods attribute");
273 }
274 BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec =
275 bsm_attr.bootstrap_method_specifiers[0];
277 if (bsm_spec.bootstrap_arguments.length != MF_ARITY) {
278 throw new Error("Bad number of static invokedynamic args " +
279 "in BootstrapMethod attribute");
280 }
282 CONSTANT_MethodHandle_info mh =
283 (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_arguments[1]);
285 boolean kindOK;
286 switch (mh.reference_kind) {
287 case REF_invokeStatic: kindOK = mk2.isStatic(); break;
288 case REF_invokeSpecial: kindOK = !mk2.isStatic(); break;
289 case REF_invokeInterface: kindOK = mk2.inInterface(); break;
290 default:
291 kindOK = false;
292 }
294 if (!kindOK) {
295 throw new Error("Bad invoke kind in implementation method handle");
296 }
298 if (!mh.getCPRefInfo().getNameAndTypeInfo().getType().toString().equals(MH_SIG)) {
299 throw new Error("Type mismatch in implementation method handle");
300 }
301 } catch (Exception e) {
302 e.printStackTrace();
303 throw new Error("error reading " + compiledTest +": " + e);
304 }
305 }
306 String makeIndyType(int id) {
307 StringBuilder buf = new StringBuilder();
308 buf.append("(");
309 if (!mk2.isStatic()) {
310 buf.append(String.format("LTest%d;", id));
311 }
312 buf.append(")Ljava/lang/Runnable;");
313 return buf.toString();
314 }
316 static final int MF_ARITY = 3;
317 static final String MH_SIG = "()V";
319 class JavaSource extends SimpleJavaFileObject {
321 static final String source_template =
322 "#CK Test#ID {\n" +
323 " #MOD1 void test() { #EK }\n" +
324 " #MOD2 void target() { }\n" +
325 "}\n";
327 String source;
329 JavaSource(int id) {
330 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
331 source = source_template.replace("#CK", mk1.ck.classStr)
332 .replace("#MOD1", mk1.mods())
333 .replace("#MOD2", mk2.mods())
334 .replace("#EK", ek.exprString)
335 .replace("#ID", String.valueOf(id));
336 }
338 @Override
339 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
340 return source;
341 }
342 }
344 static class DiagChecker
345 implements javax.tools.DiagnosticListener<JavaFileObject> {
347 boolean diagFound;
348 ArrayList<String> diags = new ArrayList<>();
350 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
351 diags.add(diagnostic.getMessage(Locale.getDefault()));
352 diagFound = true;
353 }
355 String printDiags() {
356 StringBuilder buf = new StringBuilder();
357 for (String s : diags) {
358 buf.append(s);
359 buf.append("\n");
360 }
361 return buf.toString();
362 }
363 }
365 }