TestInvokeDynamic.java revision 3019:176472b94f2e
1/* 2 * Copyright (c) 2012, 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 7194586 8003280 8006694 8010404 8129962 27 * @summary Add lambda tests 28 * Add back-end support for invokedynamic 29 * temporarily workaround combo tests are causing time out in several platforms 30 * @library /tools/javac/lib 31 * @modules jdk.jdeps/com.sun.tools.classfile 32 * jdk.compiler/com.sun.tools.javac.api 33 * jdk.compiler/com.sun.tools.javac.code 34 * jdk.compiler/com.sun.tools.javac.comp 35 * jdk.compiler/com.sun.tools.javac.main 36 * jdk.compiler/com.sun.tools.javac.jvm 37 * jdk.compiler/com.sun.tools.javac.tree 38 * jdk.compiler/com.sun.tools.javac.util 39 * @build combo.ComboTestHelper 40 * @run main TestInvokeDynamic 41 */ 42 43import java.io.IOException; 44import java.io.InputStream; 45 46import javax.tools.JavaFileObject; 47 48import com.sun.source.tree.MethodInvocationTree; 49import com.sun.source.tree.MethodTree; 50import com.sun.source.util.TaskEvent; 51import com.sun.source.util.TaskListener; 52import com.sun.source.util.TreeScanner; 53 54import com.sun.tools.classfile.Attribute; 55import com.sun.tools.classfile.BootstrapMethods_attribute; 56import com.sun.tools.classfile.ClassFile; 57import com.sun.tools.classfile.Code_attribute; 58import com.sun.tools.classfile.ConstantPool.*; 59import com.sun.tools.classfile.Instruction; 60import com.sun.tools.classfile.LineNumberTable_attribute; 61import com.sun.tools.classfile.Method; 62 63import com.sun.tools.javac.api.JavacTaskImpl; 64import com.sun.tools.javac.code.Symbol; 65import com.sun.tools.javac.code.Symbol.MethodSymbol; 66import com.sun.tools.javac.code.Symtab; 67import com.sun.tools.javac.code.Types; 68import com.sun.tools.javac.jvm.Pool; 69import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 70import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 71import com.sun.tools.javac.tree.JCTree.JCIdent; 72import com.sun.tools.javac.util.Context; 73import com.sun.tools.javac.util.Names; 74 75import combo.ComboParameter; 76import combo.ComboTask; 77import combo.ComboTestHelper; 78import combo.ComboInstance; 79import combo.ComboTask.Result; 80 81import static com.sun.tools.javac.jvm.ClassFile.*; 82 83public class TestInvokeDynamic extends ComboInstance<TestInvokeDynamic> { 84 85 enum StaticArgumentKind implements ComboParameter { 86 STRING("Hello!", "String", "Ljava/lang/String;") { 87 @Override 88 boolean check(CPInfo cpInfo) throws Exception { 89 return (cpInfo instanceof CONSTANT_String_info) && 90 ((CONSTANT_String_info)cpInfo).getString() 91 .equals(value); 92 } 93 }, 94 CLASS(null, "Class<?>", "Ljava/lang/Class;") { 95 @Override 96 boolean check(CPInfo cpInfo) throws Exception { 97 return (cpInfo instanceof CONSTANT_Class_info) && 98 ((CONSTANT_Class_info)cpInfo).getName() 99 .equals("java/lang/String"); 100 } 101 }, 102 INTEGER(1, "int", "I") { 103 @Override 104 boolean check(CPInfo cpInfo) throws Exception { 105 return (cpInfo instanceof CONSTANT_Integer_info) && 106 ((CONSTANT_Integer_info)cpInfo).value == 107 ((Integer)value).intValue(); 108 } 109 }, 110 LONG(1L, "long", "J") { 111 @Override 112 boolean check(CPInfo cpInfo) throws Exception { 113 return (cpInfo instanceof CONSTANT_Long_info) && 114 ((CONSTANT_Long_info)cpInfo).value == 115 ((Long)value).longValue(); 116 } 117 }, 118 FLOAT(1.0f, "float", "F") { 119 @Override 120 boolean check(CPInfo cpInfo) throws Exception { 121 return (cpInfo instanceof CONSTANT_Float_info) && 122 ((CONSTANT_Float_info)cpInfo).value == 123 ((Float)value).floatValue(); 124 } 125 }, 126 DOUBLE(1.0, "double","D") { 127 @Override 128 boolean check(CPInfo cpInfo) throws Exception { 129 return (cpInfo instanceof CONSTANT_Double_info) && 130 ((CONSTANT_Double_info)cpInfo).value == 131 ((Double)value).doubleValue(); 132 } 133 }, 134 METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") { 135 @Override 136 boolean check(CPInfo cpInfo) throws Exception { 137 if (!(cpInfo instanceof CONSTANT_MethodHandle_info)) 138 return false; 139 CONSTANT_MethodHandle_info handleInfo = 140 (CONSTANT_MethodHandle_info)cpInfo; 141 return handleInfo.getCPRefInfo().getClassName().equals("Array") && 142 handleInfo.reference_kind == RefKind.REF_invokeVirtual && 143 handleInfo.getCPRefInfo() 144 .getNameAndTypeInfo().getName().equals("clone") && 145 handleInfo.getCPRefInfo() 146 .getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;"); 147 } 148 }, 149 METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") { 150 @Override 151 boolean check(CPInfo cpInfo) throws Exception { 152 return (cpInfo instanceof CONSTANT_MethodType_info) && 153 ((CONSTANT_MethodType_info)cpInfo).getType() 154 .equals("()Ljava/lang/Object;"); 155 } 156 }; 157 158 Object value; 159 String sourceTypeStr; 160 String bytecodeTypeStr; 161 162 StaticArgumentKind(Object value, String sourceTypeStr, 163 String bytecodeTypeStr) { 164 this.value = value; 165 this.sourceTypeStr = sourceTypeStr; 166 this.bytecodeTypeStr = bytecodeTypeStr; 167 } 168 169 abstract boolean check(CPInfo cpInfo) throws Exception; 170 171 Object getValue(Symtab syms, Names names, Types types) { 172 switch (this) { 173 case STRING: 174 case INTEGER: 175 case LONG: 176 case FLOAT: 177 case DOUBLE: 178 return value; 179 case CLASS: 180 return syms.stringType.tsym; 181 case METHOD_HANDLE: 182 return new Pool.MethodHandle(REF_invokeVirtual, 183 syms.arrayCloneMethod, types); 184 case METHOD_TYPE: 185 return syms.arrayCloneMethod.type; 186 default: 187 throw new AssertionError(); 188 } 189 } 190 191 @Override 192 public String expand(String optParameter) { 193 return sourceTypeStr; 194 } 195 } 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"); 202 203 int arity; 204 String argsTemplate; 205 206 StaticArgumentsArity(int arity, String argsTemplate) { 207 this.arity = arity; 208 this.argsTemplate = argsTemplate; 209 } 210 211 @Override 212 public String expand(String optParameter) { 213 return argsTemplate; 214 } 215 } 216 217 public static void main(String... args) throws Exception { 218 new ComboTestHelper<TestInvokeDynamic>() 219 .withFilter(TestInvokeDynamic::redundantTestFilter) 220 .withDimension("SARGS", (x, arity) -> x.arity = arity, StaticArgumentsArity.values()) 221 .withArrayDimension("SARG", (x, arg, idx) -> x.saks[idx] = arg, 3, StaticArgumentKind.values()) 222 .run(TestInvokeDynamic::new); 223 } 224 225 StaticArgumentsArity arity; 226 StaticArgumentKind[] saks = new StaticArgumentKind[3]; 227 228 boolean redundantTestFilter() { 229 for (int i = arity.arity ; i < saks.length ; i++) { 230 if (saks[i].ordinal() != 0) { 231 return false; 232 } 233 } 234 return true; 235 } 236 237 final String source_template = 238 "import java.lang.invoke.*;\n" + 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(); 260 Context context = ct.getContext(); 261 Symtab syms = Symtab.instance(context); 262 Names names = Names.instance(context); 263 Types types = Types.instance(context); 264 ct.addTaskListener(new Indifier(syms, names, types)); 265 verifyBytecode(comboTask.generate()); 266 } 267 268 void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) { 269 if (res.hasErrors()) { 270 fail("Diags found when compiling instance: " + res.compilationInfo()); 271 return; 272 } 273 try (InputStream is = res.get().iterator().next().openInputStream()){ 274 ClassFile cf = ClassFile.read(is); 275 Method testMethod = null; 276 for (Method m : cf.methods) { 277 if (m.getName(cf.constant_pool).equals("test")) { 278 testMethod = m; 279 break; 280 } 281 } 282 if (testMethod == null) { 283 fail("Test method not found"); 284 return; 285 } 286 Code_attribute ea = 287 (Code_attribute)testMethod.attributes.get(Attribute.Code); 288 if (testMethod == null) { 289 fail("Code attribute for test() method not found"); 290 return; 291 } 292 293 int bsmIdx = -1; 294 295 for (Instruction i : ea.getInstructions()) { 296 if (i.getMnemonic().equals("invokedynamic")) { 297 CONSTANT_InvokeDynamic_info indyInfo = 298 (CONSTANT_InvokeDynamic_info)cf 299 .constant_pool.get(i.getShort(1)); 300 bsmIdx = indyInfo.bootstrap_method_attr_index; 301 if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) { 302 fail("type mismatch for CONSTANT_InvokeDynamic_info"); 303 return; 304 } 305 } 306 } 307 if (bsmIdx == -1) { 308 fail("Missing invokedynamic in generated code"); 309 return; 310 } 311 312 BootstrapMethods_attribute bsm_attr = 313 (BootstrapMethods_attribute)cf 314 .getAttribute(Attribute.BootstrapMethods); 315 if (bsm_attr.bootstrap_method_specifiers.length != 1) { 316 fail("Bad number of method specifiers " + 317 "in BootstrapMethods attribute"); 318 return; 319 } 320 BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = 321 bsm_attr.bootstrap_method_specifiers[0]; 322 323 if (bsm_spec.bootstrap_arguments.length != arity.arity) { 324 fail("Bad number of static invokedynamic args " + 325 "in BootstrapMethod attribute"); 326 return; 327 } 328 329 for (int i = 0 ; i < arity.arity ; i++) { 330 if (!saks[i].check(cf.constant_pool 331 .get(bsm_spec.bootstrap_arguments[i]))) { 332 fail("Bad static argument value " + saks[i]); 333 return; 334 } 335 } 336 337 CONSTANT_MethodHandle_info bsm_handle = 338 (CONSTANT_MethodHandle_info)cf.constant_pool 339 .get(bsm_spec.bootstrap_method_ref); 340 341 if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) { 342 fail("Bad kind on boostrap method handle"); 343 return; 344 } 345 346 CONSTANT_Methodref_info bsm_ref = 347 (CONSTANT_Methodref_info)cf.constant_pool 348 .get(bsm_handle.reference_index); 349 350 if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) { 351 fail("Bad owner of boostrap method"); 352 return; 353 } 354 355 if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) { 356 fail("Bad boostrap method name"); 357 return; 358 } 359 360 if (!bsm_ref.getNameAndTypeInfo() 361 .getType().equals(asBSMSignatureString())) { 362 fail("Bad boostrap method type" + 363 bsm_ref.getNameAndTypeInfo().getType() + " " + 364 asBSMSignatureString()); 365 return; 366 } 367 368 LineNumberTable_attribute lnt = 369 (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable); 370 371 if (lnt == null) { 372 fail("No LineNumberTable attribute"); 373 return; 374 } 375 if (lnt.line_number_table_length != 3) { 376 fail("Wrong number of entries in LineNumberTable"); 377 return; 378 } 379 } catch (Exception e) { 380 e.printStackTrace(); 381 fail("error reading classfile: " + res.compilationInfo()); 382 return; 383 } 384 } 385 386 String asBSMSignatureString() { 387 StringBuilder buf = new StringBuilder(); 388 buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"); 389 for (int i = 0 ; i < arity.arity ; i++) { 390 buf.append(saks[i].bytecodeTypeStr); 391 } 392 buf.append(")Ljava/lang/invoke/CallSite;"); 393 return buf.toString(); 394 } 395 396 class Indifier extends TreeScanner<Void, Void> implements TaskListener { 397 398 MethodSymbol bsm; 399 Symtab syms; 400 Names names; 401 Types types; 402 403 Indifier(Symtab syms, Names names, Types types) { 404 this.syms = syms; 405 this.names = names; 406 this.types = types; 407 } 408 409 @Override 410 public void started(TaskEvent e) { 411 //do nothing 412 } 413 414 @Override 415 public void finished(TaskEvent e) { 416 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 417 scan(e.getCompilationUnit(), null); 418 } 419 } 420 421 @Override 422 public Void visitMethodInvocation(MethodInvocationTree node, Void p) { 423 super.visitMethodInvocation(node, p); 424 JCMethodInvocation apply = (JCMethodInvocation)node; 425 JCIdent ident = (JCIdent)apply.meth; 426 Symbol oldSym = ident.sym; 427 if (!oldSym.isConstructor()) { 428 Object[] staticArgs = new Object[arity.arity]; 429 for (int i = 0; i < arity.arity ; i++) { 430 staticArgs[i] = saks[i].getValue(syms, names, types); 431 } 432 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name, 433 oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs); 434 } 435 return null; 436 } 437 438 @Override 439 public Void visitMethod(MethodTree node, Void p) { 440 super.visitMethod(node, p); 441 if (node.getName().toString().equals("bsm")) { 442 bsm = ((JCMethodDecl)node).sym; 443 } 444 return null; 445 } 446 } 447} 448