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 43 import java.io.IOException; 44 import java.io.InputStream; 45 46 import javax.tools.JavaFileObject; 47 48 import com.sun.source.tree.MethodInvocationTree; 49 import com.sun.source.tree.MethodTree; 50 import com.sun.source.util.TaskEvent; 51 import com.sun.source.util.TaskListener; 52 import com.sun.source.util.TreeScanner; 53 54 import com.sun.tools.classfile.Attribute; 55 import com.sun.tools.classfile.BootstrapMethods_attribute; 56 import com.sun.tools.classfile.ClassFile; 57 import com.sun.tools.classfile.Code_attribute; 58 import com.sun.tools.classfile.ConstantPool.*; 59 import com.sun.tools.classfile.Instruction; 60 import com.sun.tools.classfile.LineNumberTable_attribute; 61 import com.sun.tools.classfile.Method; 62 63 import com.sun.tools.javac.api.JavacTaskImpl; 64 import com.sun.tools.javac.code.Symbol; 65 import com.sun.tools.javac.code.Symbol.MethodSymbol; 66 import com.sun.tools.javac.code.Symtab; 67 import com.sun.tools.javac.code.Types; 68 import com.sun.tools.javac.jvm.Pool; 69 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 70 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 71 import com.sun.tools.javac.tree.JCTree.JCIdent; 72 import com.sun.tools.javac.util.Context; 73 import com.sun.tools.javac.util.Names; 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.*; 82 83 public class TestInvokeDynamic extends ComboInstance<TestInvokeDynamic> { 84 85 enum StaticArgumentKind implements ComboParameter { 86 STRING("Hello!", "String", "Ljava/lang/String;") { 87 @Override check(CPInfo cpInfo)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 }, CLASS(null, R, R)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 check(CPInfo cpInfo)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 check(CPInfo cpInfo)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 check(CPInfo cpInfo)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 check(CPInfo cpInfo)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 }, METHOD_HANDLE(null, R, R)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 }, METHOD_TYPE(null, R, R)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 StaticArgumentKind(Object value, String sourceTypeStr, String bytecodeTypeStr)162 StaticArgumentKind(Object value, String sourceTypeStr, 163 String bytecodeTypeStr) { 164 this.value = value; 165 this.sourceTypeStr = sourceTypeStr; 166 this.bytecodeTypeStr = bytecodeTypeStr; 167 } 168 check(CPInfo cpInfo)169 abstract boolean check(CPInfo cpInfo) throws Exception; 170 getValue(Symtab syms, Names names, Types types)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 expand(String optParameter)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 StaticArgumentsArity(int arity, String argsTemplate)206 StaticArgumentsArity(int arity, String argsTemplate) { 207 this.arity = arity; 208 this.argsTemplate = argsTemplate; 209 } 210 211 @Override expand(String optParameter)212 public String expand(String optParameter) { 213 return argsTemplate; 214 } 215 } 216 main(String... args)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 redundantTestFilter()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 doWork()254 public void doWork() throws IOException { 255 newCompilationTask() 256 .withOption("-g") 257 .withSourceFromTemplate(source_template) 258 .withListenerFactory(context -> { 259 Symtab syms = Symtab.instance(context); 260 Names names = Names.instance(context); 261 Types types = Types.instance(context); 262 return new Indifier(syms, names, types); 263 }) 264 .generate(this::verifyBytecode); 265 } 266 verifyBytecode(Result<Iterable<? extends JavaFileObject>> res)267 void verifyBytecode(Result<Iterable<? extends JavaFileObject>> res) { 268 if (res.hasErrors()) { 269 fail("Diags found when compiling instance: " + res.compilationInfo()); 270 return; 271 } 272 try (InputStream is = res.get().iterator().next().openInputStream()){ 273 ClassFile cf = ClassFile.read(is); 274 Method testMethod = null; 275 for (Method m : cf.methods) { 276 if (m.getName(cf.constant_pool).equals("test")) { 277 testMethod = m; 278 break; 279 } 280 } 281 if (testMethod == null) { 282 fail("Test method not found"); 283 return; 284 } 285 Code_attribute ea = 286 (Code_attribute)testMethod.attributes.get(Attribute.Code); 287 if (testMethod == null) { 288 fail("Code attribute for test() method not found"); 289 return; 290 } 291 292 int bsmIdx = -1; 293 294 for (Instruction i : ea.getInstructions()) { 295 if (i.getMnemonic().equals("invokedynamic")) { 296 CONSTANT_InvokeDynamic_info indyInfo = 297 (CONSTANT_InvokeDynamic_info)cf 298 .constant_pool.get(i.getShort(1)); 299 bsmIdx = indyInfo.bootstrap_method_attr_index; 300 if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) { 301 fail("type mismatch for CONSTANT_InvokeDynamic_info"); 302 return; 303 } 304 } 305 } 306 if (bsmIdx == -1) { 307 fail("Missing invokedynamic in generated code"); 308 return; 309 } 310 311 BootstrapMethods_attribute bsm_attr = 312 (BootstrapMethods_attribute)cf 313 .getAttribute(Attribute.BootstrapMethods); 314 if (bsm_attr.bootstrap_method_specifiers.length != 1) { 315 fail("Bad number of method specifiers " + 316 "in BootstrapMethods attribute"); 317 return; 318 } 319 BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = 320 bsm_attr.bootstrap_method_specifiers[0]; 321 322 if (bsm_spec.bootstrap_arguments.length != arity.arity) { 323 fail("Bad number of static invokedynamic args " + 324 "in BootstrapMethod attribute"); 325 return; 326 } 327 328 for (int i = 0 ; i < arity.arity ; i++) { 329 if (!saks[i].check(cf.constant_pool 330 .get(bsm_spec.bootstrap_arguments[i]))) { 331 fail("Bad static argument value " + saks[i]); 332 return; 333 } 334 } 335 336 CONSTANT_MethodHandle_info bsm_handle = 337 (CONSTANT_MethodHandle_info)cf.constant_pool 338 .get(bsm_spec.bootstrap_method_ref); 339 340 if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) { 341 fail("Bad kind on boostrap method handle"); 342 return; 343 } 344 345 CONSTANT_Methodref_info bsm_ref = 346 (CONSTANT_Methodref_info)cf.constant_pool 347 .get(bsm_handle.reference_index); 348 349 if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) { 350 fail("Bad owner of boostrap method"); 351 return; 352 } 353 354 if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) { 355 fail("Bad boostrap method name"); 356 return; 357 } 358 359 if (!bsm_ref.getNameAndTypeInfo() 360 .getType().equals(asBSMSignatureString())) { 361 fail("Bad boostrap method type" + 362 bsm_ref.getNameAndTypeInfo().getType() + " " + 363 asBSMSignatureString()); 364 return; 365 } 366 367 LineNumberTable_attribute lnt = 368 (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable); 369 370 if (lnt == null) { 371 fail("No LineNumberTable attribute"); 372 return; 373 } 374 if (lnt.line_number_table_length != 3) { 375 fail("Wrong number of entries in LineNumberTable"); 376 return; 377 } 378 } catch (Exception e) { 379 e.printStackTrace(); 380 fail("error reading classfile: " + res.compilationInfo()); 381 return; 382 } 383 } 384 asBSMSignatureString()385 String asBSMSignatureString() { 386 StringBuilder buf = new StringBuilder(); 387 buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"); 388 for (int i = 0 ; i < arity.arity ; i++) { 389 buf.append(saks[i].bytecodeTypeStr); 390 } 391 buf.append(")Ljava/lang/invoke/CallSite;"); 392 return buf.toString(); 393 } 394 395 class Indifier extends TreeScanner<Void, Void> implements TaskListener { 396 397 MethodSymbol bsm; 398 Symtab syms; 399 Names names; 400 Types types; 401 Indifier(Symtab syms, Names names, Types types)402 Indifier(Symtab syms, Names names, Types types) { 403 this.syms = syms; 404 this.names = names; 405 this.types = types; 406 } 407 408 @Override started(TaskEvent e)409 public void started(TaskEvent e) { 410 //do nothing 411 } 412 413 @Override finished(TaskEvent e)414 public void finished(TaskEvent e) { 415 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 416 scan(e.getCompilationUnit(), null); 417 } 418 } 419 420 @Override visitMethodInvocation(MethodInvocationTree node, Void p)421 public Void visitMethodInvocation(MethodInvocationTree node, Void p) { 422 super.visitMethodInvocation(node, p); 423 JCMethodInvocation apply = (JCMethodInvocation)node; 424 JCIdent ident = (JCIdent)apply.meth; 425 Symbol oldSym = ident.sym; 426 if (!oldSym.isConstructor()) { 427 Object[] staticArgs = new Object[arity.arity]; 428 for (int i = 0; i < arity.arity ; i++) { 429 staticArgs[i] = saks[i].getValue(syms, names, types); 430 } 431 ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name, 432 oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs); 433 } 434 return null; 435 } 436 437 @Override visitMethod(MethodTree node, Void p)438 public Void visitMethod(MethodTree node, Void p) { 439 super.visitMethod(node, p); 440 if (node.getName().toString().equals("bsm")) { 441 bsm = ((JCMethodDecl)node).sym; 442 } 443 return null; 444 } 445 } 446 } 447