diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index 5fca34cd3b9..0f74de5d42e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -434,6 +434,10 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem return name == name.table.names.init; } + public boolean isDynamic() { + return false; + } + /** The fully qualified name of this symbol. * This is the same as the symbol's name except for class symbols, * which are handled separately. @@ -1776,10 +1780,6 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem ClassFile.CONSTANT_InterfaceMethodref : ClassFile.CONSTANT_Methodref; } - public boolean isDynamic() { - return false; - } - public boolean isHandle() { return false; } @@ -2109,6 +2109,44 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem } } + /** A class for condy. + */ + public static class DynamicVarSymbol extends VarSymbol implements Dynamic, LoadableConstant { + public LoadableConstant[] staticArgs; + public MethodHandleSymbol bsm; + + public DynamicVarSymbol(Name name, Symbol owner, MethodHandleSymbol bsm, Type type, LoadableConstant[] staticArgs) { + super(0, name, type, owner); + this.bsm = bsm; + this.staticArgs = staticArgs; + } + + @Override + public boolean isDynamic() { + return true; + } + + @Override + public PoolConstant dynamicType() { + return type; + } + + @Override + public LoadableConstant[] staticArgs() { + return staticArgs; + } + + @Override + public LoadableConstant bootstrapMethod() { + return bsm; + } + + @Override + public int poolTag() { + return ClassFile.CONSTANT_Dynamic; + } + } + /** A class for method handles. */ public static class MethodHandleSymbol extends MethodSymbol implements LoadableConstant { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 0fa5e1b13af..868e1dbf4e6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -5202,6 +5202,8 @@ public class Types { return syms.methodHandleType; case ClassFile.CONSTANT_MethodType: return syms.methodTypeType; + case ClassFile.CONSTANT_Dynamic: + return ((DynamicVarSymbol)c).type; default: throw new AssertionError("Not a loadable constant: " + c.poolTag()); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index e79e2a10103..ed0dc1c2725 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -848,7 +848,12 @@ public class Gen extends JCTree.Visitor { // Short circuit any expressions which are constants tree.accept(classReferenceVisitor); checkStringConstant(tree.pos(), tree.type.constValue()); - result = items.makeImmediateItem(tree.type, tree.type.constValue()); + Symbol sym = TreeInfo.symbol(tree); + if (sym != null && isConstantDynamic(sym)) { + result = items.makeDynamicItem(sym); + } else { + result = items.makeImmediateItem(tree.type, tree.type.constValue()); + } } else { this.pt = pt; tree.accept(this); @@ -863,6 +868,12 @@ public class Gen extends JCTree.Visitor { } } + public boolean isConstantDynamic(Symbol sym) { + return sym.kind == VAR && + sym instanceof DynamicVarSymbol && + ((DynamicVarSymbol)sym).isDynamic(); + } + /** Derived visitor method: generate code for a list of method arguments. * @param trees The argument expressions to be visited. * @param pts The expression's expected types (i.e. the formal parameter @@ -2196,10 +2207,13 @@ public class Gen extends JCTree.Visitor { res = items.makeMemberItem(sym, true); } result = res; + } else if (isInvokeDynamic(sym) || isConstantDynamic(sym)) { + if (isConstantDynamic(sym)) { + setTypeAnnotationPositions(tree.pos); + } + result = items.makeDynamicItem(sym); } else if (sym.kind == VAR && (sym.owner.kind == MTH || sym.owner.kind == VAR)) { result = items.makeLocalItem((VarSymbol)sym); - } else if (isInvokeDynamic(sym)) { - result = items.makeDynamicItem(sym); } else if ((sym.flags() & STATIC) != 0) { if (!isAccessSuper(env.enclMethod)) sym = binaryQualifier(sym, env.enclClass.type); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java index 19a3d9be40a..c6569d7ac82 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Items.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -474,16 +474,17 @@ public class Items { } Item load() { - assert false; - return null; + Assert.check(member.kind == Kinds.Kind.VAR); + Type type = member.erasure(types); + int rescode = Code.typecode(type); + code.emitLdc((DynamicVarSymbol)member); + return stackItem[rescode]; } - void store() { - assert false; - } + void store() { Assert.error("this method shouldn't be invoked"); } Item invoke() { - // assert target.hasNativeInvokeDynamic(); + Assert.check(member.kind == Kinds.Kind.MTH); MethodType mtype = (MethodType)member.erasure(types); int rescode = Code.typecode(mtype.restype); code.emitInvokedynamic((DynamicMethodSymbol)member, mtype); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java index e9d9499dc86..0859a8f28a9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java @@ -128,7 +128,7 @@ public class PoolWriter { /** * Puts a dynamic reference into the constant pool and return its index. */ - int putDynamic(DynamicMethodSymbol d) { + int putDynamic(Dynamic d) { return pool.writeIfNeeded(d); } @@ -459,6 +459,13 @@ public class PoolWriter { poolbuf.appendChar(putNameAndType(d)); break; } + case ClassFile.CONSTANT_Dynamic: { + Symbol.DynamicVarSymbol d = (Symbol.DynamicVarSymbol)c; + poolbuf.appendByte(tag); + poolbuf.appendChar(makeBoostrapEntry(d)); + poolbuf.appendChar(putNameAndType(d)); + break; + } default: throw new AssertionError("Unexpected constant tag: " + tag); } diff --git a/test/langtools/tools/javac/T8222949/TestConstantDynamic.java b/test/langtools/tools/javac/T8222949/TestConstantDynamic.java new file mode 100644 index 00000000000..1c503c2ee51 --- /dev/null +++ b/test/langtools/tools/javac/T8222949/TestConstantDynamic.java @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8222949 + * @summary add condy support to javac's pool API + * @library /tools/javac/lib + * @modules jdk.jdeps/com.sun.tools.classfile + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.code + * jdk.compiler/com.sun.tools.javac.comp + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.jvm + * jdk.compiler/com.sun.tools.javac.tree + * jdk.compiler/com.sun.tools.javac.util + * @build combo.ComboTestHelper + * @run main TestConstantDynamic + */ + +import java.io.IOException; +import java.io.InputStream; + +import javax.tools.JavaFileObject; + +import com.sun.source.tree.*; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.source.util.TreeScanner; + +import com.sun.tools.classfile.Attribute; +import com.sun.tools.classfile.BootstrapMethods_attribute; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.Code_attribute; +import com.sun.tools.classfile.ConstantPool.*; +import com.sun.tools.classfile.Instruction; +import com.sun.tools.classfile.LineNumberTable_attribute; +import com.sun.tools.classfile.Method; + +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.Type.MethodType; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; +import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.List; + +import combo.ComboParameter; +import combo.ComboTestHelper; +import combo.ComboInstance; +import combo.ComboTask.Result; + +public class TestConstantDynamic extends ComboInstance { + + enum ConstantType implements ComboParameter { + STRING("String", "Ljava/lang/String;"), + CLASS("Class", "Ljava/lang/Class;"), + INTEGER("int", "I"), + LONG("long", "J"), + FLOAT("float", "F"), + DOUBLE("double", "D"), + METHOD_HANDLE("MethodHandle", "Ljava/lang/invoke/MethodHandle;"), + METHOD_TYPE("MethodType", "Ljava/lang/invoke/MethodType;"); + + String sourceTypeStr; + String bytecodeTypeStr; + + ConstantType(String sourceTypeStr, String bytecodeTypeStr) { + this.sourceTypeStr = sourceTypeStr; + this.bytecodeTypeStr = bytecodeTypeStr; + } + + @Override + public String expand(String optParameter) { + return sourceTypeStr; + } + } + + enum Value implements ComboParameter { + STRING("\"Hello!\""), + CLASS("null"), + INTEGER("1"), + LONG("1L"), + FLOAT("1.0f"), + DOUBLE("1.0"), + METHOD_HANDLE("null"), + METHOD_TYPE("null"); + + String value; + + Value(String value) { + this.value = value; + } + + @Override + public String expand(String optParameter) { + return value; + } + } + + public static void main(String... args) throws Exception { + new ComboTestHelper() + .withFilter(TestConstantDynamic::redundantTestFilter) + .withDimension("TYPE", (x, type) -> x.type = type, ConstantType.values()) + .withDimension("VALUE", (x, value) -> x.value = value, Value.values()) + .run(TestConstantDynamic::new); + } + + ConstantType type; + Value value; + + boolean redundantTestFilter() { + return type.name().equals(value.name()); + } + + final String source_template = + "import java.lang.invoke.*;\n" + + "import java.lang.invoke.MethodHandles.*;\n" + + "class Test {\n" + + " static final #{TYPE} f = #{VALUE};\n" + + + " static #{TYPE} bsm(MethodHandles.Lookup lookup, String name, Class type) {\n" + + " return f;\n" + + " }\n" + + + " static void test() {\n" + + " #{TYPE} i = f;\n" + + " }\n" + + "}"; + + @Override + public void doWork() throws IOException { + newCompilationTask() + .withOption("-g") + .withSourceFromTemplate(source_template) + .withListenerFactory(context -> { + Symtab syms = Symtab.instance(context); + Names names = Names.instance(context); + Types types = Types.instance(context); + return new Condifier(syms, names, types); + }) + .generate(this::verifyBytecode); + } + + void verifyBytecode(Result> res) { + if (res.hasErrors()) { + fail("Diags found when compiling instance: " + res.compilationInfo()); + return; + } + try (InputStream is = res.get().iterator().next().openInputStream()){ + ClassFile cf = ClassFile.read(is); + Method testMethod = null; + for (Method m : cf.methods) { + if (m.getName(cf.constant_pool).equals("test")) { + testMethod = m; + break; + } + } + if (testMethod == null) { + fail("Test method not found"); + return; + } + Code_attribute ea = + (Code_attribute)testMethod.attributes.get(Attribute.Code); + if (testMethod == null) { + fail("Code attribute for test() method not found"); + return; + } + + int bsmIdx = -1; + + for (Instruction i : ea.getInstructions()) { + if (i.getMnemonic().equals("ldc")) { + CONSTANT_Dynamic_info condyInfo = (CONSTANT_Dynamic_info)cf.constant_pool.get(i.getByte(1)); + bsmIdx = condyInfo.bootstrap_method_attr_index; + System.out.println("condyInfo.getNameAndTypeInfo().getType() " + condyInfo.getNameAndTypeInfo().getType()); + if (!condyInfo.getNameAndTypeInfo().getType().equals(type.bytecodeTypeStr)) { + fail("type mismatch for CONSTANT_Dynamic_info"); + return; + } + } + } + + + if (bsmIdx == -1) { + fail("Missing constantdynamic in generated code"); + return; + } + + BootstrapMethods_attribute bsm_attr = + (BootstrapMethods_attribute)cf + .getAttribute(Attribute.BootstrapMethods); + if (bsm_attr.bootstrap_method_specifiers.length != 1) { + fail("Bad number of method specifiers " + + "in BootstrapMethods attribute"); + return; + } + BootstrapMethods_attribute.BootstrapMethodSpecifier bsm_spec = + bsm_attr.bootstrap_method_specifiers[0]; + + CONSTANT_MethodHandle_info bsm_handle = + (CONSTANT_MethodHandle_info)cf.constant_pool + .get(bsm_spec.bootstrap_method_ref); + + if (bsm_handle.reference_kind != RefKind.REF_invokeStatic) { + fail("Bad kind on boostrap method handle"); + return; + } + + CONSTANT_Methodref_info bsm_ref = + (CONSTANT_Methodref_info)cf.constant_pool + .get(bsm_handle.reference_index); + + if (!bsm_ref.getClassInfo().getName().equals("Test")) { + fail("Bad owner of boostrap method"); + return; + } + + if (!bsm_ref.getNameAndTypeInfo().getName().equals("bsm")) { + fail("Bad boostrap method name"); + return; + } + + if (!bsm_ref.getNameAndTypeInfo() + .getType().equals(asBSMSignatureString())) { + fail("Bad boostrap method type" + + bsm_ref.getNameAndTypeInfo().getType() + " " + + asBSMSignatureString()); + return; + } + + LineNumberTable_attribute lnt = + (LineNumberTable_attribute)ea.attributes.get(Attribute.LineNumberTable); + + if (lnt == null) { + fail("No LineNumberTable attribute"); + return; + } + if (lnt.line_number_table_length != 2) { + fail("Wrong number of entries in LineNumberTable"); + return; + } + } catch (Exception e) { + e.printStackTrace(); + fail("error reading classfile: " + res.compilationInfo()); + return; + } + } + + String asBSMSignatureString() { + StringBuilder buf = new StringBuilder(); + buf.append("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;"); + buf.append(")" + type.bytecodeTypeStr); + return buf.toString(); + } + + class Condifier extends TreeScanner implements TaskListener { + + MethodHandleSymbol bsm; + Symtab syms; + Names names; + Types types; + + Condifier(Symtab syms, Names names, Types types) { + this.syms = syms; + this.names = names; + this.types = types; + } + + @Override + public void started(TaskEvent e) { + //do nothing + } + + @Override + public void finished(TaskEvent e) { + if (e.getKind() == TaskEvent.Kind.ANALYZE) { + scan(e.getCompilationUnit(), null); + } + } + + @Override + public Void visitVariable(VariableTree node, Void p) { + super.visitVariable(node, p); + JCVariableDecl tree = (JCVariableDecl)node; + VarSymbol v = tree.sym; + if (tree.init != null && v.name.toString().equals("i")) { + List bsm_staticArgs = List.of(syms.methodHandleLookupType, + syms.stringType, + syms.classType); + Name bsmName = names.fromString("bsm"); + Symbol.DynamicVarSymbol dynSym = new Symbol.DynamicVarSymbol(bsmName, + syms.noSymbol, + bsm, + v.type, + new LoadableConstant[0]); + ((JCIdent)tree.init).sym = dynSym; + ((JCIdent)tree.init).name = bsmName; + } + return null; + } + + @Override + public Void visitMethod(MethodTree node, Void p) { + super.visitMethod(node, p); + if (node.getName().toString().equals("bsm")) { + bsm = ((JCMethodDecl)node).sym.asHandle(); + } + return null; + } + } +}