| /* |
| * Copyright (c) 2012, 2015, 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 8129547 |
| * @summary Excess entries in BootstrapMethods with the same (bsm, bsmKind, bsmStaticArgs), but different dynamicArgs |
| * @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.jvm |
| * jdk.compiler/com.sun.tools.javac.tree |
| * jdk.compiler/com.sun.tools.javac.util |
| */ |
| |
| import java.io.File; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Locale; |
| |
| import javax.tools.Diagnostic; |
| import javax.tools.JavaCompiler; |
| import javax.tools.JavaFileObject; |
| import javax.tools.SimpleJavaFileObject; |
| import javax.tools.ToolProvider; |
| |
| import com.sun.source.tree.MethodInvocationTree; |
| import com.sun.source.tree.MethodTree; |
| 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.javac.api.JavacTaskImpl; |
| import com.sun.tools.javac.code.Symbol; |
| import com.sun.tools.javac.code.Symbol.MethodSymbol; |
| import com.sun.tools.javac.code.Symtab; |
| import com.sun.tools.javac.code.Types; |
| import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; |
| import com.sun.tools.javac.tree.JCTree.JCMethodDecl; |
| import com.sun.tools.javac.tree.JCTree.JCIdent; |
| import com.sun.tools.javac.util.Context; |
| import com.sun.tools.javac.util.Names; |
| |
| import static com.sun.tools.javac.jvm.ClassFile.*; |
| |
| public class TestBootstrapMethodsCount { |
| |
| public static void main(String... args) throws Exception { |
| JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); |
| new TestBootstrapMethodsCount().run(comp); |
| } |
| |
| DiagChecker dc; |
| |
| TestBootstrapMethodsCount() { |
| dc = new DiagChecker(); |
| } |
| |
| public void run(JavaCompiler comp) { |
| JavaSource source = new JavaSource(); |
| JavacTaskImpl ct = (JavacTaskImpl)comp.getTask(null, null, dc, |
| Arrays.asList("-g"), null, Arrays.asList(source)); |
| Context context = ct.getContext(); |
| Symtab syms = Symtab.instance(context); |
| Names names = Names.instance(context); |
| Types types = Types.instance(context); |
| ct.addTaskListener(new Indifier(syms, names, types)); |
| try { |
| ct.generate(); |
| } catch (Throwable t) { |
| t.printStackTrace(); |
| throw new AssertionError( |
| String.format("Error thrown when compiling following code\n%s", |
| source.source)); |
| } |
| if (dc.diagFound) { |
| throw new AssertionError( |
| String.format("Diags found when compiling following code\n%s\n\n%s", |
| source.source, dc.printDiags())); |
| } |
| verifyBytecode(); |
| } |
| |
| void verifyBytecode() { |
| File compiledTest = new File("Test.class"); |
| try { |
| ClassFile cf = ClassFile.read(compiledTest); |
| BootstrapMethods_attribute bsm_attr = |
| (BootstrapMethods_attribute)cf |
| .getAttribute(Attribute.BootstrapMethods); |
| int length = bsm_attr.bootstrap_method_specifiers.length; |
| if (length != 1) { |
| throw new Error("Bad number of method specifiers " + |
| "in BootstrapMethods attribute: " + length); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new Error("error reading " + compiledTest +": " + e); |
| } |
| } |
| |
| class JavaSource extends SimpleJavaFileObject { |
| |
| static final String source = "import java.lang.invoke.*;\n" + |
| "class Bootstrap {\n" + |
| " public static CallSite bsm(MethodHandles.Lookup lookup, " + |
| "String name, MethodType methodType) {\n" + |
| " return null;\n" + |
| " }\n" + |
| "}\n" + |
| "class Test {\n" + |
| " void m1() { }\n" + |
| " void m2(Object arg1) { }\n" + |
| " void test1() {\n" + |
| " Object o = this; // marker statement \n" + |
| " m1();\n" + |
| " }\n" + |
| " void test2(Object arg1) {\n" + |
| " Object o = this; // marker statement \n" + |
| " m2(arg1);\n" + |
| " }\n" + |
| "}"; |
| |
| JavaSource() { |
| super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); |
| } |
| |
| @Override |
| public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
| return source; |
| } |
| } |
| |
| class Indifier extends TreeScanner<Void, Void> implements TaskListener { |
| |
| MethodSymbol bsm; |
| Symtab syms; |
| Names names; |
| Types types; |
| |
| Indifier(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 visitMethodInvocation(MethodInvocationTree node, Void p) { |
| super.visitMethodInvocation(node, p); |
| JCMethodInvocation apply = (JCMethodInvocation)node; |
| JCIdent ident = (JCIdent)apply.meth; |
| Symbol oldSym = ident.sym; |
| if (!oldSym.isConstructor()) { |
| ident.sym = new Symbol.DynamicMethodSymbol(oldSym.name, |
| oldSym.owner, REF_invokeStatic, bsm, oldSym.type, new Object[0]); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitMethod(MethodTree node, Void p) { |
| super.visitMethod(node, p); |
| if (node.getName().toString().equals("bsm")) { |
| bsm = ((JCMethodDecl)node).sym; |
| } |
| return null; |
| } |
| } |
| |
| static class DiagChecker |
| implements javax.tools.DiagnosticListener<JavaFileObject> { |
| |
| boolean diagFound; |
| ArrayList<String> diags = new ArrayList<>(); |
| |
| public void report(Diagnostic<? extends JavaFileObject> diagnostic) { |
| diags.add(diagnostic.getMessage(Locale.getDefault())); |
| diagFound = true; |
| } |
| |
| String printDiags() { |
| StringBuilder buf = new StringBuilder(); |
| for (String s : diags) { |
| buf.append(s); |
| buf.append("\n"); |
| } |
| return buf.toString(); |
| } |
| } |
| |
| } |