21 * questions. |
21 * questions. |
22 */ |
22 */ |
23 |
23 |
24 /* |
24 /* |
25 * @test |
25 * @test |
26 * @bug 7194586 8003280 8006694 8010404 |
26 * @bug 7194586 8003280 8006694 8010404 8129962 |
27 * @summary Add lambda tests |
27 * @summary Add lambda tests |
28 * Add back-end support for invokedynamic |
28 * Add back-end support for invokedynamic |
29 * temporarily workaround combo tests are causing time out in several platforms |
29 * temporarily workaround combo tests are causing time out in several platforms |
30 * @library ../lib |
30 * @library /tools/javac/lib |
31 * @modules jdk.jdeps/com.sun.tools.classfile |
31 * @modules jdk.jdeps/com.sun.tools.classfile |
32 * jdk.compiler/com.sun.tools.javac.api |
32 * jdk.compiler/com.sun.tools.javac.api |
33 * jdk.compiler/com.sun.tools.javac.code |
33 * jdk.compiler/com.sun.tools.javac.code |
|
34 * jdk.compiler/com.sun.tools.javac.comp |
|
35 * jdk.compiler/com.sun.tools.javac.main |
34 * jdk.compiler/com.sun.tools.javac.jvm |
36 * jdk.compiler/com.sun.tools.javac.jvm |
35 * jdk.compiler/com.sun.tools.javac.tree |
37 * jdk.compiler/com.sun.tools.javac.tree |
36 * jdk.compiler/com.sun.tools.javac.util |
38 * jdk.compiler/com.sun.tools.javac.util |
37 * @build JavacTestingAbstractThreadedTest |
39 * @build combo.ComboTestHelper |
38 * @run main/othervm TestInvokeDynamic |
40 * @run main TestInvokeDynamic |
39 */ |
41 */ |
40 |
42 |
41 // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) |
43 import java.io.IOException; |
42 // see JDK-8006746 |
44 import java.io.InputStream; |
43 |
45 |
44 import java.io.File; |
|
45 import java.net.URI; |
|
46 import java.util.ArrayList; |
|
47 import java.util.Arrays; |
|
48 import java.util.Locale; |
|
49 |
|
50 import javax.tools.Diagnostic; |
|
51 import javax.tools.JavaFileObject; |
46 import javax.tools.JavaFileObject; |
52 import javax.tools.SimpleJavaFileObject; |
|
53 |
47 |
54 import com.sun.source.tree.MethodInvocationTree; |
48 import com.sun.source.tree.MethodInvocationTree; |
55 import com.sun.source.tree.MethodTree; |
49 import com.sun.source.tree.MethodTree; |
56 import com.sun.source.util.TaskEvent; |
50 import com.sun.source.util.TaskEvent; |
57 import com.sun.source.util.TaskListener; |
51 import com.sun.source.util.TaskListener; |
76 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; |
70 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; |
77 import com.sun.tools.javac.tree.JCTree.JCIdent; |
71 import com.sun.tools.javac.tree.JCTree.JCIdent; |
78 import com.sun.tools.javac.util.Context; |
72 import com.sun.tools.javac.util.Context; |
79 import com.sun.tools.javac.util.Names; |
73 import com.sun.tools.javac.util.Names; |
80 |
74 |
|
75 import combo.ComboParameter; |
|
76 import combo.ComboTask; |
|
77 import combo.ComboTestHelper; |
|
78 import combo.ComboInstance; |
|
79 import combo.ComboTask.Result; |
|
80 |
81 import static com.sun.tools.javac.jvm.ClassFile.*; |
81 import static com.sun.tools.javac.jvm.ClassFile.*; |
82 |
82 |
83 public class TestInvokeDynamic |
83 public class TestInvokeDynamic extends ComboInstance<TestInvokeDynamic> { |
84 extends JavacTestingAbstractThreadedTest |
84 |
85 implements Runnable { |
85 enum StaticArgumentKind implements ComboParameter { |
86 |
|
87 enum StaticArgumentKind { |
|
88 STRING("Hello!", "String", "Ljava/lang/String;") { |
86 STRING("Hello!", "String", "Ljava/lang/String;") { |
89 @Override |
87 @Override |
90 boolean check(CPInfo cpInfo) throws Exception { |
88 boolean check(CPInfo cpInfo) throws Exception { |
91 return (cpInfo instanceof CONSTANT_String_info) && |
89 return (cpInfo instanceof CONSTANT_String_info) && |
92 ((CONSTANT_String_info)cpInfo).getString() |
90 ((CONSTANT_String_info)cpInfo).getString() |
187 return syms.arrayCloneMethod.type; |
185 return syms.arrayCloneMethod.type; |
188 default: |
186 default: |
189 throw new AssertionError(); |
187 throw new AssertionError(); |
190 } |
188 } |
191 } |
189 } |
192 } |
190 |
193 |
191 @Override |
194 enum StaticArgumentsArity { |
192 public String expand(String optParameter) { |
195 ZERO(0), |
193 return sourceTypeStr; |
196 ONE(1), |
194 } |
197 TWO(2), |
195 } |
198 THREE(3); |
196 |
|
197 enum StaticArgumentsArity implements ComboParameter { |
|
198 ZERO(0, ""), |
|
199 ONE(1, ",#{SARG[0]} s1"), |
|
200 TWO(2, ",#{SARG[0]} s1, #{SARG[1]} s2"), |
|
201 THREE(3, ",#{SARG[0]} s1, #{SARG[1]} s2, #{SARG[2]} s3"); |
199 |
202 |
200 int arity; |
203 int arity; |
201 |
204 String argsTemplate; |
202 StaticArgumentsArity(int arity) { |
205 |
|
206 StaticArgumentsArity(int arity, String argsTemplate) { |
203 this.arity = arity; |
207 this.arity = arity; |
|
208 this.argsTemplate = argsTemplate; |
|
209 } |
|
210 |
|
211 @Override |
|
212 public String expand(String optParameter) { |
|
213 return argsTemplate; |
204 } |
214 } |
205 } |
215 } |
206 |
216 |
207 public static void main(String... args) throws Exception { |
217 public static void main(String... args) throws Exception { |
208 for (StaticArgumentsArity arity : StaticArgumentsArity.values()) { |
218 new ComboTestHelper<TestInvokeDynamic>() |
209 if (arity.arity == 0) { |
219 .withFilter(TestInvokeDynamic::redundantTestFilter) |
210 pool.execute(new TestInvokeDynamic(arity)); |
220 .withDimension("SARGS", (x, arity) -> x.arity = arity, StaticArgumentsArity.values()) |
211 } else { |
221 .withArrayDimension("SARG", (x, arg, idx) -> x.saks[idx] = arg, 3, StaticArgumentKind.values()) |
212 for (StaticArgumentKind sak1 : StaticArgumentKind.values()) { |
222 .run(TestInvokeDynamic::new); |
213 if (arity.arity == 1) { |
|
214 pool.execute(new TestInvokeDynamic(arity, sak1)); |
|
215 } else { |
|
216 for (StaticArgumentKind sak2 : StaticArgumentKind.values()) { |
|
217 if (arity.arity == 2) { |
|
218 pool.execute(new TestInvokeDynamic(arity, sak1, sak2)); |
|
219 } else { |
|
220 for (StaticArgumentKind sak3 : StaticArgumentKind.values()) { |
|
221 pool.execute( |
|
222 new TestInvokeDynamic(arity, sak1, sak2, sak3)); |
|
223 } |
|
224 } |
|
225 } |
|
226 } |
|
227 } |
|
228 } |
|
229 } |
|
230 |
|
231 checkAfterExec(); |
|
232 } |
223 } |
233 |
224 |
234 StaticArgumentsArity arity; |
225 StaticArgumentsArity arity; |
235 StaticArgumentKind[] saks; |
226 StaticArgumentKind[] saks = new StaticArgumentKind[3]; |
236 DiagChecker dc; |
227 |
237 |
228 boolean redundantTestFilter() { |
238 TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) { |
229 for (int i = arity.arity ; i < saks.length ; i++) { |
239 this.arity = arity; |
230 if (saks[i].ordinal() != 0) { |
240 this.saks = saks; |
231 return false; |
241 dc = new DiagChecker(); |
232 } |
242 } |
233 } |
243 |
234 return true; |
244 public void run() { |
235 } |
245 int id = checkCount.incrementAndGet(); |
236 |
246 JavaSource source = new JavaSource(id); |
237 final String source_template = |
247 JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, fm.get(), dc, |
238 "import java.lang.invoke.*;\n" + |
248 Arrays.asList("-g"), null, Arrays.asList(source)); |
239 "class Test {\n" + |
|
240 " void m() { }\n" + |
|
241 " void test() {\n" + |
|
242 " Object o = this; // marker statement \n" + |
|
243 " m();\n" + |
|
244 " }\n" + |
|
245 "}\n" + |
|
246 "class Bootstrap {\n" + |
|
247 " public static CallSite bsm(MethodHandles.Lookup lookup, " + |
|
248 "String name, MethodType methodType #{SARGS}) {\n" + |
|
249 " return null;\n" + |
|
250 " }\n" + |
|
251 "}"; |
|
252 |
|
253 @Override |
|
254 public void doWork() throws IOException { |
|
255 ComboTask comboTask = newCompilationTask() |
|
256 .withOption("-g") |
|
257 .withSourceFromTemplate(source_template); |
|
258 |
|
259 JavacTaskImpl ct = (JavacTaskImpl)comboTask.getTask(); |
249 Context context = ct.getContext(); |
260 Context context = ct.getContext(); |
250 Symtab syms = Symtab.instance(context); |
261 Symtab syms = Symtab.instance(context); |
251 Names names = Names.instance(context); |
262 Names names = Names.instance(context); |
252 Types types = Types.instance(context); |
263 Types types = Types.instance(context); |
253 ct.addTaskListener(new Indifier(syms, names, types)); |
264 ct.addTaskListener(new Indifier(syms, names, types)); |
254 try { |
265 verifyBytecode(comboTask.generate()); |
255 ct.generate(); |
266 } |
256 } catch (Throwable t) { |
267 |
257 t.printStackTrace(); |
268 void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) { |
258 throw new AssertionError( |
269 if (res.hasErrors()) { |
259 String.format("Error thrown when compiling following code\n%s", |
270 fail("Diags found when compiling instance: " + res.compilationInfo()); |
260 source.source)); |
271 return; |
261 } |
272 } |
262 if (dc.diagFound) { |
273 try (InputStream is = res.get().iterator().next().openInputStream()){ |
263 throw new AssertionError( |
274 ClassFile cf = ClassFile.read(is); |
264 String.format("Diags found when compiling following code\n%s\n\n%s", |
|
265 source.source, dc.printDiags())); |
|
266 } |
|
267 verifyBytecode(id); |
|
268 } |
|
269 |
|
270 void verifyBytecode(int id) { |
|
271 File compiledTest = new File(String.format("Test%d.class", id)); |
|
272 try { |
|
273 ClassFile cf = ClassFile.read(compiledTest); |
|
274 Method testMethod = null; |
275 Method testMethod = null; |
275 for (Method m : cf.methods) { |
276 for (Method m : cf.methods) { |
276 if (m.getName(cf.constant_pool).equals("test")) { |
277 if (m.getName(cf.constant_pool).equals("test")) { |
277 testMethod = m; |
278 testMethod = m; |
278 break; |
279 break; |
279 } |
280 } |
280 } |
281 } |
281 if (testMethod == null) { |
282 if (testMethod == null) { |
282 throw new Error("Test method not found"); |
283 fail("Test method not found"); |
|
284 return; |
283 } |
285 } |
284 Code_attribute ea = |
286 Code_attribute ea = |
285 (Code_attribute)testMethod.attributes.get(Attribute.Code); |
287 (Code_attribute)testMethod.attributes.get(Attribute.Code); |
286 if (testMethod == null) { |
288 if (testMethod == null) { |
287 throw new Error("Code attribute for test() method not found"); |
289 fail("Code attribute for test() method not found"); |
|
290 return; |
288 } |
291 } |
289 |
292 |
290 int bsmIdx = -1; |
293 int bsmIdx = -1; |
291 |
294 |
292 for (Instruction i : ea.getInstructions()) { |
295 for (Instruction i : ea.getInstructions()) { |
294 CONSTANT_InvokeDynamic_info indyInfo = |
297 CONSTANT_InvokeDynamic_info indyInfo = |
295 (CONSTANT_InvokeDynamic_info)cf |
298 (CONSTANT_InvokeDynamic_info)cf |
296 .constant_pool.get(i.getShort(1)); |
299 .constant_pool.get(i.getShort(1)); |
297 bsmIdx = indyInfo.bootstrap_method_attr_index; |
300 bsmIdx = indyInfo.bootstrap_method_attr_index; |
298 if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) { |
301 if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) { |
299 throw new |
302 fail("type mismatch for CONSTANT_InvokeDynamic_info"); |
300 AssertionError("type mismatch for CONSTANT_InvokeDynamic_info"); |
303 return; |
301 } |
304 } |
302 } |
305 } |
303 } |
306 } |
304 if (bsmIdx == -1) { |
307 if (bsmIdx == -1) { |
305 throw new Error("Missing invokedynamic in generated code"); |
308 fail("Missing invokedynamic in generated code"); |
|
309 return; |
306 } |
310 } |
307 |
311 |
308 BootstrapMethods_attribute bsm_attr = |
312 BootstrapMethods_attribute bsm_attr = |
309 (BootstrapMethods_attribute)cf |
313 (BootstrapMethods_attribute)cf |
310 .getAttribute(Attribute.BootstrapMethods); |
314 .getAttribute(Attribute.BootstrapMethods); |
311 if (bsm_attr.bootstrap_method_specifiers.length != 1) { |
315 if (bsm_attr.bootstrap_method_specifiers.length != 1) { |
312 throw new Error("Bad number of method specifiers " + |
316 fail("Bad number of method specifiers " + |
313 "in BootstrapMethods attribute"); |
317 "in BootstrapMethods attribute"); |
|
318 return; |
314 } |
319 } |
315 BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = |
320 BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = |
316 bsm_attr.bootstrap_method_specifiers[0]; |
321 bsm_attr.bootstrap_method_specifiers[0]; |
317 |
322 |
318 if (bsm_spec.bootstrap_arguments.length != arity.arity) { |
323 if (bsm_spec.bootstrap_arguments.length != arity.arity) { |
319 throw new Error("Bad number of static invokedynamic args " + |
324 fail("Bad number of static invokedynamic args " + |
320 "in BootstrapMethod attribute"); |
325 "in BootstrapMethod attribute"); |
321 } |
326 return; |
322 |
327 } |
323 int count = 0; |
328 |
324 for (StaticArgumentKind sak : saks) { |
329 for (int i = 0 ; i < arity.arity ; i++) { |
325 if (!sak.check(cf.constant_pool |
330 if (!saks[i].check(cf.constant_pool |
326 .get(bsm_spec.bootstrap_arguments[count]))) { |
331 .get(bsm_spec.bootstrap_arguments[i]))) { |
327 throw new Error("Bad static argument value " + sak); |
332 fail("Bad static argument value " + saks[i]); |
|
333 return; |
328 } |
334 } |
329 count++; |
|
330 } |
335 } |
331 |
336 |
332 CONSTANT_MethodHandle_info bsm_handle = |
337 CONSTANT_MethodHandle_info bsm_handle = |
333 (CONSTANT_MethodHandle_info)cf.constant_pool |
338 (CONSTANT_MethodHandle_info)cf.constant_pool |
334 .get(bsm_spec.bootstrap_method_ref); |
339 .get(bsm_spec.bootstrap_method_ref); |
335 |
340 |
336 if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) { |
341 if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) { |
337 throw new Error("Bad kind on boostrap method handle"); |
342 fail("Bad kind on boostrap method handle"); |
|
343 return; |
338 } |
344 } |
339 |
345 |
340 CONSTANT_Methodref_info bsm_ref = |
346 CONSTANT_Methodref_info bsm_ref = |
341 (CONSTANT_Methodref_info)cf.constant_pool |
347 (CONSTANT_Methodref_info)cf.constant_pool |
342 .get(bsm_handle.reference_index); |
348 .get(bsm_handle.reference_index); |
343 |
349 |
344 if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) { |
350 if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) { |
345 throw new Error("Bad owner of boostrap method"); |
351 fail("Bad owner of boostrap method"); |
|
352 return; |
346 } |
353 } |
347 |
354 |
348 if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) { |
355 if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) { |
349 throw new Error("Bad boostrap method name"); |
356 fail("Bad boostrap method name"); |
|
357 return; |
350 } |
358 } |
351 |
359 |
352 if (!bsm_ref.getNameAndTypeInfo() |
360 if (!bsm_ref.getNameAndTypeInfo() |
353 .getType().equals(asBSMSignatureString())) { |
361 .getType().equals(asBSMSignatureString())) { |
354 throw new Error("Bad boostrap method type" + |
362 fail("Bad boostrap method type" + |
355 bsm_ref.getNameAndTypeInfo().getType() + " " + |
363 bsm_ref.getNameAndTypeInfo().getType() + " " + |
356 asBSMSignatureString()); |
364 asBSMSignatureString()); |
|
365 return; |
357 } |
366 } |
358 |
367 |
359 LineNumberTable_attribute lnt = |
368 LineNumberTable_attribute lnt = |
360 (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable); |
369 (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable); |
361 |
370 |
362 if (lnt == null) { |
371 if (lnt == null) { |
363 throw new Error("No LineNumberTable attribute"); |
372 fail("No LineNumberTable attribute"); |
|
373 return; |
364 } |
374 } |
365 if (lnt.line_number_table_length != 3) { |
375 if (lnt.line_number_table_length != 3) { |
366 throw new Error("Wrong number of entries in LineNumberTable"); |
376 fail("Wrong number of entries in LineNumberTable"); |
|
377 return; |
367 } |
378 } |
368 } catch (Exception e) { |
379 } catch (Exception e) { |
369 e.printStackTrace(); |
380 e.printStackTrace(); |
370 throw new Error("error reading " + compiledTest +": " + e); |
381 fail("error reading classfile: " + res.compilationInfo()); |
|
382 return; |
371 } |
383 } |
372 } |
384 } |
373 |
385 |
374 String asBSMSignatureString() { |
386 String asBSMSignatureString() { |
375 StringBuilder buf = new StringBuilder(); |
387 StringBuilder buf = new StringBuilder(); |
376 buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"); |
388 buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"); |
377 for (StaticArgumentKind sak : saks) { |
389 for (int i = 0 ; i < arity.arity ; i++) { |
378 buf.append(sak.bytecodeTypeStr); |
390 buf.append(saks[i].bytecodeTypeStr); |
379 } |
391 } |
380 buf.append(")Ljava/lang/invoke/CallSite;"); |
392 buf.append(")Ljava/lang/invoke/CallSite;"); |
381 return buf.toString(); |
393 return buf.toString(); |
382 } |
|
383 |
|
384 class JavaSource extends SimpleJavaFileObject { |
|
385 |
|
386 static final String source_template = "import java.lang.invoke.*;\n" + |
|
387 "class Bootstrap {\n" + |
|
388 " public static CallSite bsm(MethodHandles.Lookup lookup, " + |
|
389 "String name, MethodType methodType #SARGS) {\n" + |
|
390 " return null;\n" + |
|
391 " }\n" + |
|
392 "}\n" + |
|
393 "class Test#ID {\n" + |
|
394 " void m() { }\n" + |
|
395 " void test() {\n" + |
|
396 " Object o = this; // marker statement \n" + |
|
397 " m();\n" + |
|
398 " }\n" + |
|
399 "}"; |
|
400 |
|
401 String source; |
|
402 |
|
403 JavaSource(int id) { |
|
404 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); |
|
405 source = source_template.replace("#SARGS", asSignatureString()) |
|
406 .replace("#ID", String.valueOf(id)); |
|
407 } |
|
408 |
|
409 @Override |
|
410 public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
|
411 return source; |
|
412 } |
|
413 |
|
414 String asSignatureString() { |
|
415 int count = 0; |
|
416 StringBuilder buf = new StringBuilder(); |
|
417 for (StaticArgumentKind sak : saks) { |
|
418 buf.append(","); |
|
419 buf.append(sak.sourceTypeStr); |
|
420 buf.append(' '); |
|
421 buf.append(String.format("x%d", count++)); |
|
422 } |
|
423 return buf.toString(); |
|
424 } |
|
425 } |
394 } |
426 |
395 |
427 class Indifier extends TreeScanner<Void, Void> implements TaskListener { |
396 class Indifier extends TreeScanner<Void, Void> implements TaskListener { |
428 |
397 |
429 MethodSymbol bsm; |
398 MethodSymbol bsm; |