Tue, 25 Sep 2012 11:53:18 +0100
7194586: Add back-end support for invokedynamic
Summary: Add support for invokedynamic bytecode instruction; includes suppot for generation of all related classfile attributes
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java Tue Sep 25 11:52:37 2012 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java Tue Sep 25 11:53:18 2012 +0100 1.3 @@ -1068,6 +1068,10 @@ 1.4 } 1.5 } 1.6 1.7 + public boolean isDynamic() { 1.8 + return false; 1.9 + } 1.10 + 1.11 /** find a symbol that this (proxy method) symbol implements. 1.12 * @param c The class whose members are searched for 1.13 * implementations 1.14 @@ -1356,6 +1360,27 @@ 1.15 } 1.16 } 1.17 1.18 + /** A class for invokedynamic method calls. 1.19 + */ 1.20 + public static class DynamicMethodSymbol extends MethodSymbol { 1.21 + 1.22 + public Object[] staticArgs; 1.23 + public Symbol bsm; 1.24 + public int bsmKind; 1.25 + 1.26 + public DynamicMethodSymbol(Name name, Symbol owner, int bsmKind, MethodSymbol bsm, Type type, Object[] staticArgs) { 1.27 + super(0, name, type, owner); 1.28 + this.bsm = bsm; 1.29 + this.bsmKind = bsmKind; 1.30 + this.staticArgs = staticArgs; 1.31 + } 1.32 + 1.33 + @Override 1.34 + public boolean isDynamic() { 1.35 + return true; 1.36 + } 1.37 + } 1.38 + 1.39 /** A class for predefined operators. 1.40 */ 1.41 public static class OperatorSymbol extends MethodSymbol {
2.1 --- a/src/share/classes/com/sun/tools/javac/code/Symtab.java Tue Sep 25 11:52:37 2012 +0100 2.2 +++ b/src/share/classes/com/sun/tools/javac/code/Symtab.java Tue Sep 25 11:53:18 2012 +0100 2.3 @@ -126,6 +126,7 @@ 2.4 public final Type cloneableType; 2.5 public final Type serializableType; 2.6 public final Type methodHandleType; 2.7 + public final Type methodTypeType; 2.8 public final Type nativeHeaderType; 2.9 public final Type throwableType; 2.10 public final Type errorType; 2.11 @@ -440,6 +441,7 @@ 2.12 throwableType = enterClass("java.lang.Throwable"); 2.13 serializableType = enterClass("java.io.Serializable"); 2.14 methodHandleType = enterClass("java.lang.invoke.MethodHandle"); 2.15 + methodTypeType = enterClass("java.lang.invoke.MethodType"); 2.16 errorType = enterClass("java.lang.Error"); 2.17 illegalArgumentExceptionType = enterClass("java.lang.IllegalArgumentException"); 2.18 interruptedExceptionType = enterClass("java.lang.InterruptedException");
3.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java Tue Sep 25 11:52:37 2012 +0100 3.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java Tue Sep 25 11:53:18 2012 +0100 3.3 @@ -84,6 +84,16 @@ 3.4 public final static int CONSTANT_MethodType = 16; 3.5 public final static int CONSTANT_InvokeDynamic = 18; 3.6 3.7 + public final static int REF_getField = 1; 3.8 + public final static int REF_getStatic = 2; 3.9 + public final static int REF_putField = 3; 3.10 + public final static int REF_putStatic = 4; 3.11 + public final static int REF_invokeVirtual = 5; 3.12 + public final static int REF_invokeStatic = 6; 3.13 + public final static int REF_invokeSpecial = 7; 3.14 + public final static int REF_newInvokeSpecial = 8; 3.15 + public final static int REF_invokeInterface = 9; 3.16 + 3.17 public final static int MAX_PARAMETERS = 0xff; 3.18 public final static int MAX_DIMENSIONS = 0xff; 3.19 public final static int MAX_CODE = 0xffff;
4.1 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Tue Sep 25 11:52:37 2012 +0100 4.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Tue Sep 25 11:53:18 2012 +0100 4.3 @@ -26,6 +26,8 @@ 4.4 package com.sun.tools.javac.jvm; 4.5 4.6 import java.io.*; 4.7 +import java.util.LinkedHashMap; 4.8 +import java.util.Map; 4.9 import java.util.Set; 4.10 import java.util.HashSet; 4.11 4.12 @@ -137,6 +139,11 @@ 4.13 */ 4.14 ListBuffer<ClassSymbol> innerClassesQueue; 4.15 4.16 + /** The bootstrap methods to be written in the corresponding class attribute 4.17 + * (one for each invokedynamic) 4.18 + */ 4.19 + Map<MethodSymbol, Pool.MethodHandle> bootstrapMethods; 4.20 + 4.21 /** The log to use for verbose output. 4.22 */ 4.23 private final Log log; 4.24 @@ -477,11 +484,27 @@ 4.25 4.26 if (value instanceof MethodSymbol) { 4.27 MethodSymbol m = (MethodSymbol)value; 4.28 - poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0 4.29 - ? CONSTANT_InterfaceMethodref 4.30 - : CONSTANT_Methodref); 4.31 - poolbuf.appendChar(pool.put(m.owner)); 4.32 - poolbuf.appendChar(pool.put(nameType(m))); 4.33 + if (!m.isDynamic()) { 4.34 + poolbuf.appendByte((m.owner.flags() & INTERFACE) != 0 4.35 + ? CONSTANT_InterfaceMethodref 4.36 + : CONSTANT_Methodref); 4.37 + poolbuf.appendChar(pool.put(m.owner)); 4.38 + poolbuf.appendChar(pool.put(nameType(m))); 4.39 + } else { 4.40 + //invokedynamic 4.41 + DynamicMethodSymbol dynSym = (DynamicMethodSymbol)m; 4.42 + Pool.MethodHandle handle = new Pool.MethodHandle(dynSym.bsmKind, dynSym.bsm, names); 4.43 + bootstrapMethods.put(dynSym, handle); 4.44 + //init cp entries 4.45 + pool.put(names.BootstrapMethods); 4.46 + pool.put(handle); 4.47 + for (Object staticArg : dynSym.staticArgs) { 4.48 + pool.put(staticArg); 4.49 + } 4.50 + poolbuf.appendByte(CONSTANT_InvokeDynamic); 4.51 + poolbuf.appendChar(bootstrapMethods.size() - 1); 4.52 + poolbuf.appendChar(pool.put(nameType(dynSym))); 4.53 + } 4.54 } else if (value instanceof VarSymbol) { 4.55 VarSymbol v = (VarSymbol)value; 4.56 poolbuf.appendByte(CONSTANT_Fieldref); 4.57 @@ -526,11 +549,20 @@ 4.58 } else if (value instanceof String) { 4.59 poolbuf.appendByte(CONSTANT_String); 4.60 poolbuf.appendChar(pool.put(names.fromString((String)value))); 4.61 + } else if (value instanceof MethodType) { 4.62 + MethodType mtype = (MethodType)value; 4.63 + poolbuf.appendByte(CONSTANT_MethodType); 4.64 + poolbuf.appendChar(pool.put(typeSig(mtype))); 4.65 } else if (value instanceof Type) { 4.66 Type type = (Type)value; 4.67 if (type.tag == CLASS) enterInner((ClassSymbol)type.tsym); 4.68 poolbuf.appendByte(CONSTANT_Class); 4.69 poolbuf.appendChar(pool.put(xClassName(type))); 4.70 + } else if (value instanceof Pool.MethodHandle) { 4.71 + Pool.MethodHandle ref = (Pool.MethodHandle)value; 4.72 + poolbuf.appendByte(CONSTANT_MethodHandle); 4.73 + poolbuf.appendByte(ref.refKind); 4.74 + poolbuf.appendChar(pool.put(ref.refSym)); 4.75 } else { 4.76 Assert.error("writePool " + value); 4.77 } 4.78 @@ -914,6 +946,25 @@ 4.79 endAttr(alenIdx); 4.80 } 4.81 4.82 + /** Write "bootstrapMethods" attribute. 4.83 + */ 4.84 + void writeBootstrapMethods() { 4.85 + int alenIdx = writeAttr(names.BootstrapMethods); 4.86 + databuf.appendChar(bootstrapMethods.size()); 4.87 + for (Map.Entry<MethodSymbol, Pool.MethodHandle> entry : bootstrapMethods.entrySet()) { 4.88 + DynamicMethodSymbol dsym = (DynamicMethodSymbol)entry.getKey(); 4.89 + //write BSM handle 4.90 + databuf.appendChar(pool.get(entry.getValue())); 4.91 + //write static args length 4.92 + databuf.appendChar(dsym.staticArgs.length); 4.93 + //write static args array 4.94 + for (Object o : dsym.staticArgs) { 4.95 + databuf.appendChar(pool.get(o)); 4.96 + } 4.97 + } 4.98 + endAttr(alenIdx); 4.99 + } 4.100 + 4.101 /** Write field symbol, entering all references into constant pool. 4.102 */ 4.103 void writeField(VarSymbol v) { 4.104 @@ -1483,6 +1534,7 @@ 4.105 pool = c.pool; 4.106 innerClasses = null; 4.107 innerClassesQueue = null; 4.108 + bootstrapMethods = new LinkedHashMap<MethodSymbol, Pool.MethodHandle>(); 4.109 4.110 Type supertype = types.supertype(c.type); 4.111 List<Type> interfaces = types.interfaces(c.type); 4.112 @@ -1589,6 +1641,12 @@ 4.113 writeInnerClasses(); 4.114 acount++; 4.115 } 4.116 + 4.117 + if (!bootstrapMethods.isEmpty()) { 4.118 + writeBootstrapMethods(); 4.119 + acount++; 4.120 + } 4.121 + 4.122 endAttrs(acountIdx, acount); 4.123 4.124 poolbuf.appendBytes(databuf.elems, 0, databuf.length);
5.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Code.java Tue Sep 25 11:52:37 2012 +0100 5.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Code.java Tue Sep 25 11:53:18 2012 +0100 5.3 @@ -903,6 +903,8 @@ 5.4 if (o instanceof Double) return syms.doubleType; 5.5 if (o instanceof ClassSymbol) return syms.classType; 5.6 if (o instanceof Type.ArrayType) return syms.classType; 5.7 + if (o instanceof Type.MethodType) return syms.methodTypeType; 5.8 + if (o instanceof Pool.MethodHandle) return syms.methodHandleType; 5.9 throw new AssertionError(o); 5.10 } 5.11
6.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Gen.java Tue Sep 25 11:52:37 2012 +0100 6.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Gen.java Tue Sep 25 11:53:18 2012 +0100 6.3 @@ -2103,6 +2103,8 @@ 6.4 result = res; 6.5 } else if (sym.kind == VAR && sym.owner.kind == MTH) { 6.6 result = items.makeLocalItem((VarSymbol)sym); 6.7 + } else if (isInvokeDynamic(sym)) { 6.8 + result = items.makeDynamicItem(sym); 6.9 } else if ((sym.flags() & STATIC) != 0) { 6.10 if (!isAccessSuper(env.enclMethod)) 6.11 sym = binaryQualifier(sym, env.enclClass.type); 6.12 @@ -2152,8 +2154,12 @@ 6.13 result = items. 6.14 makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue()); 6.15 } else { 6.16 - if (!accessSuper) 6.17 + if (isInvokeDynamic(sym)) { 6.18 + result = items.makeDynamicItem(sym); 6.19 + return; 6.20 + } else if (!accessSuper) { 6.21 sym = binaryQualifier(sym, tree.selected.type); 6.22 + } 6.23 if ((sym.flags() & STATIC) != 0) { 6.24 if (!selectSuper && (ssym == null || ssym.kind != TYP)) 6.25 base = base.load(); 6.26 @@ -2174,6 +2180,10 @@ 6.27 } 6.28 } 6.29 6.30 + public boolean isInvokeDynamic(Symbol sym) { 6.31 + return sym.kind == MTH && ((MethodSymbol)sym).isDynamic(); 6.32 + } 6.33 + 6.34 public void visitLiteral(JCLiteral tree) { 6.35 if (tree.type.tag == TypeTags.BOT) { 6.36 code.emitop0(aconst_null);
7.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Items.java Tue Sep 25 11:52:37 2012 +0100 7.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Items.java Tue Sep 25 11:53:18 2012 +0100 7.3 @@ -1,5 +1,5 @@ 7.4 /* 7.5 - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. 7.6 + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. 7.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.8 * 7.9 * This code is free software; you can redistribute it and/or modify it 7.10 @@ -110,6 +110,13 @@ 7.11 return stackItem[Code.typecode(type)]; 7.12 } 7.13 7.14 + /** Make an item representing a dynamically invoked method. 7.15 + * @param member The represented symbol. 7.16 + */ 7.17 + Item makeDynamicItem(Symbol member) { 7.18 + return new DynamicItem(member); 7.19 + } 7.20 + 7.21 /** Make an item representing an indexed expression. 7.22 * @param type The expression's type. 7.23 */ 7.24 @@ -457,6 +464,35 @@ 7.25 } 7.26 } 7.27 7.28 + /** An item representing a dynamic call site. 7.29 + */ 7.30 + class DynamicItem extends StaticItem { 7.31 + DynamicItem(Symbol member) { 7.32 + super(member); 7.33 + } 7.34 + 7.35 + Item load() { 7.36 + assert false; 7.37 + return null; 7.38 + } 7.39 + 7.40 + void store() { 7.41 + assert false; 7.42 + } 7.43 + 7.44 + Item invoke() { 7.45 + // assert target.hasNativeInvokeDynamic(); 7.46 + MethodType mtype = (MethodType)member.erasure(types); 7.47 + int rescode = Code.typecode(mtype.restype); 7.48 + code.emitInvokedynamic(pool.put(member), mtype); 7.49 + return stackItem[rescode]; 7.50 + } 7.51 + 7.52 + public String toString() { 7.53 + return "dynamic(" + member + ")"; 7.54 + } 7.55 + } 7.56 + 7.57 /** An item representing an instance variable or method. 7.58 */ 7.59 class MemberItem extends Item {
8.1 --- a/src/share/classes/com/sun/tools/javac/jvm/Pool.java Tue Sep 25 11:52:37 2012 +0100 8.2 +++ b/src/share/classes/com/sun/tools/javac/jvm/Pool.java Tue Sep 25 11:53:18 2012 +0100 8.3 @@ -25,9 +25,17 @@ 8.4 8.5 package com.sun.tools.javac.jvm; 8.6 8.7 +import com.sun.tools.javac.code.Flags; 8.8 +import com.sun.tools.javac.code.Kinds; 8.9 import java.util.*; 8.10 8.11 +import com.sun.tools.javac.code.Symbol; 8.12 import com.sun.tools.javac.code.Symbol.*; 8.13 +import com.sun.tools.javac.code.Type; 8.14 +import com.sun.tools.javac.util.Assert; 8.15 +import com.sun.tools.javac.util.Filter; 8.16 +import com.sun.tools.javac.util.Name; 8.17 +import com.sun.tools.javac.util.Names; 8.18 8.19 /** An internal structure that corresponds to the constant pool of a classfile. 8.20 * 8.21 @@ -167,4 +175,90 @@ 8.22 v.type.hashCode(); 8.23 } 8.24 } 8.25 + 8.26 + public static class MethodHandle { 8.27 + 8.28 + /** Reference kind - see ClassFile */ 8.29 + int refKind; 8.30 + 8.31 + /** Reference symbol */ 8.32 + Symbol refSym; 8.33 + 8.34 + /** Reference to the name table */ 8.35 + Names names; 8.36 + 8.37 + public MethodHandle(int refKind, Symbol refSym, Names names) { 8.38 + this.refKind = refKind; 8.39 + this.refSym = refSym; 8.40 + this.names = names; 8.41 + checkConsistent(); 8.42 + } 8.43 + public boolean equals(Object other) { 8.44 + if (!(other instanceof MethodHandle)) return false; 8.45 + MethodHandle mr = (MethodHandle) other; 8.46 + if (mr.refKind != refKind) return false; 8.47 + Symbol o = mr.refSym; 8.48 + return 8.49 + o.name == refSym.name && 8.50 + o.owner == refSym.owner && 8.51 + o.type.equals(refSym.type); 8.52 + } 8.53 + public int hashCode() { 8.54 + return 8.55 + refKind * 65 + 8.56 + refSym.name.hashCode() * 33 + 8.57 + refSym.owner.hashCode() * 9 + 8.58 + refSym.type.hashCode(); 8.59 + } 8.60 + 8.61 + /** 8.62 + * Check consistency of reference kind and symbol (see JVMS 4.4.8) 8.63 + */ 8.64 + @SuppressWarnings("fallthrough") 8.65 + private void checkConsistent() { 8.66 + boolean staticOk = false; 8.67 + int expectedKind = -1; 8.68 + Filter<Name> nameFilter = nonInitFilter; 8.69 + boolean interfaceOwner = false; 8.70 + switch (refKind) { 8.71 + case ClassFile.REF_getStatic: 8.72 + case ClassFile.REF_putStatic: 8.73 + staticOk = true; 8.74 + case ClassFile.REF_getField: 8.75 + case ClassFile.REF_putField: 8.76 + expectedKind = Kinds.VAR; 8.77 + break; 8.78 + case ClassFile.REF_newInvokeSpecial: 8.79 + nameFilter = initFilter; 8.80 + expectedKind = Kinds.MTH; 8.81 + break; 8.82 + case ClassFile.REF_invokeInterface: 8.83 + interfaceOwner = true; 8.84 + expectedKind = Kinds.MTH; 8.85 + break; 8.86 + case ClassFile.REF_invokeStatic: 8.87 + staticOk = true; 8.88 + case ClassFile.REF_invokeVirtual: 8.89 + case ClassFile.REF_invokeSpecial: 8.90 + expectedKind = Kinds.MTH; 8.91 + break; 8.92 + } 8.93 + Assert.check(!refSym.isStatic() || staticOk); 8.94 + Assert.check(refSym.kind == expectedKind); 8.95 + Assert.check(nameFilter.accepts(refSym.name)); 8.96 + Assert.check(!refSym.owner.isInterface() || interfaceOwner); 8.97 + } 8.98 + //where 8.99 + Filter<Name> nonInitFilter = new Filter<Name>() { 8.100 + public boolean accepts(Name n) { 8.101 + return n != names.init && n != names.clinit; 8.102 + } 8.103 + }; 8.104 + 8.105 + Filter<Name> initFilter = new Filter<Name>() { 8.106 + public boolean accepts(Name n) { 8.107 + return n == names.init; 8.108 + } 8.109 + }; 8.110 + } 8.111 }
9.1 --- a/src/share/classes/com/sun/tools/javac/util/Names.java Tue Sep 25 11:52:37 2012 +0100 9.2 +++ b/src/share/classes/com/sun/tools/javac/util/Names.java Tue Sep 25 11:53:18 2012 +0100 9.3 @@ -169,6 +169,9 @@ 9.4 public final Name ex; 9.5 public final Name package_info; 9.6 9.7 + // lambda-related 9.8 + public final Name BootstrapMethods; 9.9 + 9.10 public final Name.Table table; 9.11 9.12 public Names(Context context) { 9.13 @@ -296,6 +299,9 @@ 9.14 deprecated = fromString("deprecated"); 9.15 ex = fromString("ex"); 9.16 package_info = fromString("package-info"); 9.17 + 9.18 + //lambda-related 9.19 + BootstrapMethods = fromString("BootstrapMethods"); 9.20 } 9.21 9.22 protected Name.Table createTable(Options options) {
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/test/tools/javac/lambda/TestInvokeDynamic.java Tue Sep 25 11:53:18 2012 +0100 10.3 @@ -0,0 +1,442 @@ 10.4 +/* 10.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 10.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.7 + * 10.8 + * This code is free software; you can redistribute it and/or modify it 10.9 + * under the terms of the GNU General Public License version 2 only, as 10.10 + * published by the Free Software Foundation. 10.11 + * 10.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 10.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 10.15 + * version 2 for more details (a copy is included in the LICENSE file that 10.16 + * accompanied this code). 10.17 + * 10.18 + * You should have received a copy of the GNU General Public License version 10.19 + * 2 along with this work; if not, write to the Free Software Foundation, 10.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 10.21 + * 10.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 10.23 + * or visit www.oracle.com if you need additional information or have any 10.24 + * questions. 10.25 + */ 10.26 + 10.27 +/* 10.28 + * @test 10.29 + * @bug 7194586 10.30 + * 10.31 + * @summary Add back-end support for invokedynamic 10.32 + * 10.33 + */ 10.34 + 10.35 +import com.sun.source.tree.MethodInvocationTree; 10.36 +import com.sun.source.tree.MethodTree; 10.37 +import com.sun.source.util.TaskEvent; 10.38 +import com.sun.source.util.TaskListener; 10.39 +import com.sun.source.util.TreeScanner; 10.40 + 10.41 +import com.sun.tools.classfile.Attribute; 10.42 +import com.sun.tools.classfile.BootstrapMethods_attribute; 10.43 +import com.sun.tools.classfile.ClassFile; 10.44 +import com.sun.tools.classfile.Code_attribute; 10.45 +import com.sun.tools.classfile.ConstantPool.*; 10.46 +import com.sun.tools.classfile.Instruction; 10.47 +import com.sun.tools.classfile.Method; 10.48 + 10.49 +import com.sun.tools.javac.api.JavacTaskImpl; 10.50 +import com.sun.tools.javac.api.JavacTool; 10.51 +import com.sun.tools.javac.code.Symbol; 10.52 +import com.sun.tools.javac.code.Symbol.MethodSymbol; 10.53 +import com.sun.tools.javac.code.Symtab; 10.54 +import com.sun.tools.javac.jvm.Pool; 10.55 +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 10.56 +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 10.57 +import com.sun.tools.javac.tree.JCTree.JCIdent; 10.58 +import com.sun.tools.javac.util.Context; 10.59 +import com.sun.tools.javac.util.Names; 10.60 + 10.61 +import java.io.File; 10.62 +import java.net.URI; 10.63 +import java.util.ArrayList; 10.64 +import java.util.Arrays; 10.65 +import java.util.Locale; 10.66 + 10.67 +import javax.tools.Diagnostic; 10.68 +import javax.tools.JavaCompiler; 10.69 +import javax.tools.JavaFileManager; 10.70 +import javax.tools.JavaFileObject; 10.71 +import javax.tools.SimpleJavaFileObject; 10.72 +import javax.tools.StandardJavaFileManager; 10.73 +import javax.tools.ToolProvider; 10.74 + 10.75 +import static com.sun.tools.javac.jvm.ClassFile.*; 10.76 + 10.77 +public class TestInvokeDynamic { 10.78 + 10.79 + static int checkCount = 0; 10.80 + 10.81 + enum StaticArgumentKind { 10.82 + STRING("Hello!", "String", "Ljava/lang/String;") { 10.83 + @Override 10.84 + boolean check(CPInfo cpInfo) throws Exception { 10.85 + return (cpInfo instanceof CONSTANT_String_info) && 10.86 + ((CONSTANT_String_info)cpInfo).getString().equals(value); 10.87 + } 10.88 + }, 10.89 + CLASS(null, "Class<?>", "Ljava/lang/Class;") { 10.90 + @Override 10.91 + boolean check(CPInfo cpInfo) throws Exception { 10.92 + return (cpInfo instanceof CONSTANT_Class_info) && 10.93 + ((CONSTANT_Class_info)cpInfo).getName().equals("java/lang/String"); 10.94 + } 10.95 + }, 10.96 + INTEGER(1, "int", "I") { 10.97 + @Override 10.98 + boolean check(CPInfo cpInfo) throws Exception { 10.99 + return (cpInfo instanceof CONSTANT_Integer_info) && 10.100 + ((CONSTANT_Integer_info)cpInfo).value == ((Integer)value).intValue(); 10.101 + } 10.102 + }, 10.103 + LONG(1L, "long", "J") { 10.104 + @Override 10.105 + boolean check(CPInfo cpInfo) throws Exception { 10.106 + return (cpInfo instanceof CONSTANT_Long_info) && 10.107 + ((CONSTANT_Long_info)cpInfo).value == ((Long)value).longValue(); 10.108 + } 10.109 + }, 10.110 + FLOAT(1.0f, "float", "F") { 10.111 + @Override 10.112 + boolean check(CPInfo cpInfo) throws Exception { 10.113 + return (cpInfo instanceof CONSTANT_Float_info) && 10.114 + ((CONSTANT_Float_info)cpInfo).value == ((Float)value).floatValue(); 10.115 + } 10.116 + }, 10.117 + DOUBLE(1.0, "double","D") { 10.118 + @Override 10.119 + boolean check(CPInfo cpInfo) throws Exception { 10.120 + return (cpInfo instanceof CONSTANT_Double_info) && 10.121 + ((CONSTANT_Double_info)cpInfo).value == ((Double)value).doubleValue(); 10.122 + } 10.123 + }, 10.124 + METHOD_HANDLE(null, "MethodHandle", "Ljava/lang/invoke/MethodHandle;") { 10.125 + @Override 10.126 + boolean check(CPInfo cpInfo) throws Exception { 10.127 + if (!(cpInfo instanceof CONSTANT_MethodHandle_info)) return false; 10.128 + CONSTANT_MethodHandle_info handleInfo = (CONSTANT_MethodHandle_info)cpInfo; 10.129 + return handleInfo.getCPRefInfo().getClassName().equals("Array") && 10.130 + handleInfo.reference_kind == RefKind.REF_invokeVirtual && 10.131 + handleInfo.getCPRefInfo().getNameAndTypeInfo().getName().equals("clone") && 10.132 + handleInfo.getCPRefInfo().getNameAndTypeInfo().getType().equals("()Ljava/lang/Object;"); 10.133 + } 10.134 + }, 10.135 + METHOD_TYPE(null, "MethodType", "Ljava/lang/invoke/MethodType;") { 10.136 + @Override 10.137 + boolean check(CPInfo cpInfo) throws Exception { 10.138 + return (cpInfo instanceof CONSTANT_MethodType_info) && 10.139 + ((CONSTANT_MethodType_info)cpInfo).getType().equals("()Ljava/lang/Object;"); 10.140 + } 10.141 + }; 10.142 + 10.143 + Object value; 10.144 + String sourceTypeStr; 10.145 + String bytecodeTypeStr; 10.146 + 10.147 + StaticArgumentKind(Object value, String sourceTypeStr, String bytecodeTypeStr) { 10.148 + this.value = value; 10.149 + this.sourceTypeStr = sourceTypeStr; 10.150 + this.bytecodeTypeStr = bytecodeTypeStr; 10.151 + } 10.152 + 10.153 + abstract boolean check(CPInfo cpInfo) throws Exception; 10.154 + 10.155 + Object getValue(Symtab syms, Names names) { 10.156 + switch (this) { 10.157 + case STRING: 10.158 + case INTEGER: 10.159 + case LONG: 10.160 + case FLOAT: 10.161 + case DOUBLE: 10.162 + return value; 10.163 + case CLASS: 10.164 + return syms.stringType.tsym; 10.165 + case METHOD_HANDLE: 10.166 + return new Pool.MethodHandle(REF_invokeVirtual, syms.arrayCloneMethod, names); 10.167 + case METHOD_TYPE: 10.168 + return syms.arrayCloneMethod.type; 10.169 + default: 10.170 + throw new AssertionError(); 10.171 + } 10.172 + } 10.173 + } 10.174 + 10.175 + enum StaticArgumentsArity { 10.176 + ZERO(0), 10.177 + ONE(1), 10.178 + TWO(2), 10.179 + THREE(3); 10.180 + 10.181 + int arity; 10.182 + 10.183 + StaticArgumentsArity(int arity) { 10.184 + this.arity = arity; 10.185 + } 10.186 + } 10.187 + 10.188 + public static void main(String... args) throws Exception { 10.189 + // Create a single file manager and compiler and reuse it for each compile to save time. 10.190 + StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null); 10.191 + final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); 10.192 + for (StaticArgumentsArity arity : StaticArgumentsArity.values()) { 10.193 + if (arity.arity == 0) { 10.194 + new TestInvokeDynamic(arity).compileAndCheck(fm, tool); 10.195 + } else { 10.196 + for (StaticArgumentKind sak1 : StaticArgumentKind.values()) { 10.197 + if (arity.arity == 1) { 10.198 + new TestInvokeDynamic(arity, sak1).compileAndCheck(fm, tool); 10.199 + } else { 10.200 + for (StaticArgumentKind sak2 : StaticArgumentKind.values()) { 10.201 + if (arity.arity == 2) { 10.202 + new TestInvokeDynamic(arity, sak1, sak2).compileAndCheck(fm, tool); 10.203 + } else { 10.204 + for (StaticArgumentKind sak3 : StaticArgumentKind.values()) { 10.205 + new TestInvokeDynamic(arity, sak1, sak2, sak3).compileAndCheck(fm, tool); 10.206 + } 10.207 + } 10.208 + } 10.209 + } 10.210 + } 10.211 + } 10.212 + } 10.213 + 10.214 + System.out.println("Total checks made: " + checkCount); 10.215 + } 10.216 + 10.217 + StaticArgumentsArity arity; 10.218 + StaticArgumentKind[] saks; 10.219 + JavaSource source; 10.220 + DiagChecker dc; 10.221 + 10.222 + TestInvokeDynamic(StaticArgumentsArity arity, StaticArgumentKind... saks) { 10.223 + this.arity = arity; 10.224 + this.saks = saks; 10.225 + source = new JavaSource(); 10.226 + dc = new DiagChecker(); 10.227 + } 10.228 + 10.229 + void compileAndCheck(JavaFileManager fm, JavaCompiler tool) throws Exception { 10.230 + JavacTaskImpl ct = (JavacTaskImpl)tool.getTask(null, fm, dc, 10.231 + null, null, Arrays.asList(source)); 10.232 + Context context = ct.getContext(); 10.233 + Symtab syms = Symtab.instance(context); 10.234 + Names names = Names.instance(context); 10.235 + ct.addTaskListener(new Indifier(syms, names)); 10.236 + try { 10.237 + ct.generate(); 10.238 + } catch (Throwable t) { 10.239 + t.printStackTrace(); 10.240 + throw new AssertionError(String.format("Error thrown when compiling following code\n%s", source.source)); 10.241 + } 10.242 + if (dc.diagFound) { 10.243 + throw new AssertionError(String.format("Diags found when compiling following code\n%s\n\n%s", source.source, dc.printDiags())); 10.244 + } 10.245 + verifyBytecode(); 10.246 + } 10.247 + 10.248 + void verifyBytecode() { 10.249 + File compiledTest = new File("Test.class"); 10.250 + try { 10.251 + ClassFile cf = ClassFile.read(compiledTest); 10.252 + Method testMethod = null; 10.253 + for (Method m : cf.methods) { 10.254 + if (m.getName(cf.constant_pool).equals("test")) { 10.255 + testMethod = m; 10.256 + break; 10.257 + } 10.258 + } 10.259 + if (testMethod == null) { 10.260 + throw new Error("Test method not found"); 10.261 + } 10.262 + Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code); 10.263 + if (testMethod == null) { 10.264 + throw new Error("Code attribute for test() method not found"); 10.265 + } 10.266 + 10.267 + int bsmIdx = -1; 10.268 + 10.269 + for (Instruction i : ea.getInstructions()) { 10.270 + if (i.getMnemonic().equals("invokedynamic")) { 10.271 + CONSTANT_InvokeDynamic_info indyInfo = 10.272 + (CONSTANT_InvokeDynamic_info)cf.constant_pool.get(i.getShort(1)); 10.273 + bsmIdx = indyInfo.bootstrap_method_attr_index; 10.274 + if (!indyInfo.getNameAndTypeInfo().getType().equals("()V")) { 10.275 + throw new AssertionError("type mismatch for CONSTANT_InvokeDynamic_info"); 10.276 + } 10.277 + } 10.278 + } 10.279 + if (bsmIdx == -1) { 10.280 + throw new Error("Missing invokedynamic in generated code"); 10.281 + } 10.282 + 10.283 + BootstrapMethods_attribute bsm_attr = (BootstrapMethods_attribute)cf.getAttribute(Attribute.BootstrapMethods); 10.284 + if (bsm_attr.bootstrap_method_specifiers.length != 1) { 10.285 + throw new Error("Bad number of method specifiers in BootstrapMethods attribute"); 10.286 + } 10.287 + BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = 10.288 + bsm_attr.bootstrap_method_specifiers[0]; 10.289 + 10.290 + if (bsm_spec.bootstrap_arguments.length != arity.arity) { 10.291 + throw new Error("Bad number of static invokedynamic args in BootstrapMethod attribute"); 10.292 + } 10.293 + 10.294 + int count = 0; 10.295 + for (StaticArgumentKind sak : saks) { 10.296 + if (!sak.check(cf.constant_pool.get(bsm_spec.bootstrap_arguments[count]))) { 10.297 + throw new Error("Bad static argument value " + sak); 10.298 + } 10.299 + count++; 10.300 + } 10.301 + 10.302 + CONSTANT_MethodHandle_info bsm_handle = 10.303 + (CONSTANT_MethodHandle_info)cf.constant_pool.get(bsm_spec.bootstrap_method_ref); 10.304 + 10.305 + if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) { 10.306 + throw new Error("Bad kind on boostrap method handle"); 10.307 + } 10.308 + 10.309 + CONSTANT_Methodref_info bsm_ref = 10.310 + (CONSTANT_Methodref_info)cf.constant_pool.get(bsm_handle.reference_index); 10.311 + 10.312 + if (!bsm_ref.getClassInfo().getName().equals("Bootstrap")) { 10.313 + throw new Error("Bad owner of boostrap method"); 10.314 + } 10.315 + 10.316 + if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) { 10.317 + throw new Error("Bad boostrap method name"); 10.318 + } 10.319 + 10.320 + if (!bsm_ref.getNameAndTypeInfo().getType().equals(asBSMSignatureString())) { 10.321 + throw new Error("Bad boostrap method type" + bsm_ref.getNameAndTypeInfo().getType() + " " + asBSMSignatureString()); 10.322 + } 10.323 + } catch (Exception e) { 10.324 + e.printStackTrace(); 10.325 + throw new Error("error reading " + compiledTest +": " + e); 10.326 + } 10.327 + } 10.328 + 10.329 + String asBSMSignatureString() { 10.330 + StringBuilder buf = new StringBuilder(); 10.331 + buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"); 10.332 + for (StaticArgumentKind sak : saks) { 10.333 + buf.append(sak.bytecodeTypeStr); 10.334 + } 10.335 + buf.append(")Ljava/lang/invoke/CallSite;"); 10.336 + return buf.toString(); 10.337 + } 10.338 + 10.339 + class JavaSource extends SimpleJavaFileObject { 10.340 + 10.341 + static final String source_template = "import java.lang.invoke.*;\n" + 10.342 + "class Bootstrap {\n" + 10.343 + " public static CallSite bsm(MethodHandles.Lookup lookup, String name, MethodType methodType #SARGS) {\n" + 10.344 + " return null;\n" + 10.345 + " }\n" + 10.346 + "}\n" + 10.347 + "class Test {\n" + 10.348 + " void m() { }\n" + 10.349 + " void test() { m(); }\n" + 10.350 + "}"; 10.351 + 10.352 + String source; 10.353 + 10.354 + JavaSource() { 10.355 + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); 10.356 + source = source_template.replace("#SARGS", asSignatureString()); 10.357 + } 10.358 + 10.359 + @Override 10.360 + public CharSequence getCharContent(boolean ignoreEncodingErrors) { 10.361 + return source; 10.362 + } 10.363 + 10.364 + String asSignatureString() { 10.365 + int count = 0; 10.366 + StringBuilder buf = new StringBuilder(); 10.367 + for (StaticArgumentKind sak : saks) { 10.368 + buf.append(","); 10.369 + buf.append(sak.sourceTypeStr); 10.370 + buf.append(' '); 10.371 + buf.append(String.format("x%d", count++)); 10.372 + } 10.373 + return buf.toString(); 10.374 + } 10.375 + } 10.376 + 10.377 + class Indifier extends TreeScanner<Void, Void> implements TaskListener { 10.378 + 10.379 + MethodSymbol bsm; 10.380 + Symtab syms; 10.381 + Names names; 10.382 + 10.383 + Indifier(Symtab syms, Names names) { 10.384 + this.syms = syms; 10.385 + this.names = names; 10.386 + } 10.387 + 10.388 + @Override 10.389 + public void started(TaskEvent e) { 10.390 + //do nothing 10.391 + } 10.392 + 10.393 + @Override 10.394 + public void finished(TaskEvent e) { 10.395 + if (e.getKind() == TaskEvent.Kind.ANALYZE) { 10.396 + scan(e.getCompilationUnit(), null); 10.397 + } 10.398 + } 10.399 + 10.400 + @Override 10.401 + public Void visitMethodInvocation(MethodInvocationTree node, Void p) { 10.402 + super.visitMethodInvocation(node, p); 10.403 + JCMethodInvocation apply = (JCMethodInvocation)node; 10.404 + JCIdent ident = (JCIdent)apply.meth; 10.405 + Symbol oldSym = ident.sym; 10.406 + if (!oldSym.isConstructor()) { 10.407 + Object[] staticArgs = new Object[arity.arity]; 10.408 + for (int i = 0; i < arity.arity ; i++) { 10.409 + staticArgs[i] = saks[i].getValue(syms, names); 10.410 + } 10.411 + ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name, oldSym.owner, REF_invokeStatic, bsm, oldSym.type, staticArgs); 10.412 + } 10.413 + return null; 10.414 + } 10.415 + 10.416 + @Override 10.417 + public Void visitMethod(MethodTree node, Void p) { 10.418 + super.visitMethod(node, p); 10.419 + if (node.getName().toString().equals("bsm")) { 10.420 + bsm = ((JCMethodDecl)node).sym; 10.421 + } 10.422 + return null; 10.423 + } 10.424 + } 10.425 + 10.426 + static class DiagChecker implements javax.tools.DiagnosticListener<JavaFileObject> { 10.427 + 10.428 + boolean diagFound; 10.429 + ArrayList<String> diags = new ArrayList<>(); 10.430 + 10.431 + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 10.432 + diags.add(diagnostic.getMessage(Locale.getDefault())); 10.433 + diagFound = true; 10.434 + } 10.435 + 10.436 + String printDiags() { 10.437 + StringBuilder buf = new StringBuilder(); 10.438 + for (String s : diags) { 10.439 + buf.append(s); 10.440 + buf.append("\n"); 10.441 + } 10.442 + return buf.toString(); 10.443 + } 10.444 + } 10.445 +}