blob: 27501dad4aaf413d4b267afeded86e475fa5f6ea [file] [log] [blame]
/*
* 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();
}
}
}