1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/tools/javac/generics/bridges/BridgeHarness.java Thu Jul 11 14:07:39 2013 +0100 1.3 @@ -0,0 +1,218 @@ 1.4 +/* 1.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + */ 1.26 + 1.27 +/* 1.28 + * @test 1.29 + * @bug 8013789 1.30 + * @summary Compiler should emit bridges in interfaces 1.31 + * @library /tools/javac/lib 1.32 + * @build JavacTestingAbstractProcessor BridgeHarness 1.33 + * @run main BridgeHarness 1.34 + */ 1.35 + 1.36 +import com.sun.source.util.JavacTask; 1.37 +import com.sun.tools.classfile.AccessFlags; 1.38 +import com.sun.tools.classfile.ClassFile; 1.39 +import com.sun.tools.classfile.ConstantPool; 1.40 +import com.sun.tools.classfile.ConstantPoolException; 1.41 +import com.sun.tools.classfile.Method; 1.42 +import com.sun.tools.javac.code.Symbol.ClassSymbol; 1.43 +import com.sun.tools.javac.util.List; 1.44 + 1.45 +import java.io.File; 1.46 +import java.util.Arrays; 1.47 +import java.util.Collections; 1.48 +import java.util.HashMap; 1.49 +import java.util.Map; 1.50 +import java.util.Set; 1.51 + 1.52 +import javax.annotation.processing.RoundEnvironment; 1.53 +import javax.annotation.processing.SupportedAnnotationTypes; 1.54 +import javax.lang.model.element.Element; 1.55 +import javax.lang.model.element.TypeElement; 1.56 +import javax.tools.JavaCompiler; 1.57 +import javax.tools.JavaFileObject; 1.58 +import javax.tools.StandardJavaFileManager; 1.59 +import javax.tools.ToolProvider; 1.60 + 1.61 +import static javax.tools.StandardLocation.*; 1.62 + 1.63 +public class BridgeHarness { 1.64 + 1.65 + /** number of errors found (must be zero for the test to pass) */ 1.66 + static int nerrors = 0; 1.67 + 1.68 + /** the (shared) Java compiler used for compiling the tests */ 1.69 + static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 1.70 + 1.71 + /** the (shared) file manager used by the compiler */ 1.72 + static final StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); 1.73 + 1.74 + public static void main(String[] args) throws Exception { 1.75 + //set sourcepath 1.76 + fm.setLocation(SOURCE_PATH, 1.77 + Arrays.asList(new File(System.getProperty("test.src"), "tests"))); 1.78 + //set output (-d) 1.79 + fm.setLocation(javax.tools.StandardLocation.CLASS_OUTPUT, 1.80 + Arrays.asList(new File(System.getProperty("user.dir")))); 1.81 + for (JavaFileObject jfo : fm.list(SOURCE_PATH, "", Collections.singleton(JavaFileObject.Kind.SOURCE), true)) { 1.82 + //for each source, compile and check against annotations 1.83 + new BridgeHarness(jfo).compileAndCheck(); 1.84 + } 1.85 + //if there were errors, fail 1.86 + if (nerrors > 0) { 1.87 + throw new AssertionError("Errors were found"); 1.88 + } 1.89 + } 1.90 + 1.91 + /* utility methods */ 1.92 + 1.93 + /** 1.94 + * Remove an element from a list 1.95 + */ 1.96 + static <Z> List<Z> drop(List<Z> lz, Z z) { 1.97 + if (lz.head == z) { 1.98 + return drop(lz.tail, z); 1.99 + } else if (lz.isEmpty()) { 1.100 + return lz; 1.101 + } else { 1.102 + return drop(lz.tail, z).prepend(lz.head); 1.103 + } 1.104 + } 1.105 + 1.106 + /** 1.107 + * return a string representation of a bytecode method 1.108 + */ 1.109 + static String descriptor(Method m, ConstantPool cp) throws ConstantPoolException { 1.110 + return m.getName(cp) + m.descriptor.getValue(cp); 1.111 + } 1.112 + 1.113 + /* test harness */ 1.114 + 1.115 + /** Test file to be compiled */ 1.116 + JavaFileObject jfo; 1.117 + 1.118 + /** Mapping between class name and list of bridges in class with that name */ 1.119 + Map<String, List<Bridge>> bridgesMap = new HashMap<String, List<Bridge>>(); 1.120 + 1.121 + protected BridgeHarness(JavaFileObject jfo) { 1.122 + this.jfo = jfo; 1.123 + } 1.124 + 1.125 + /** 1.126 + * Compile a test using a custom annotation processor and check the generated 1.127 + * bytecode against discovered annotations. 1.128 + */ 1.129 + protected void compileAndCheck() throws Exception { 1.130 + JavacTask ct = (JavacTask)comp.getTask(null, fm, null, null, null, Arrays.asList(jfo)); 1.131 + ct.setProcessors(Collections.singleton(new BridgeFinder())); 1.132 + 1.133 + for (JavaFileObject jfo : ct.generate()) { 1.134 + checkBridges(jfo); 1.135 + } 1.136 + } 1.137 + 1.138 + /** 1.139 + * Check that every bridge in the generated classfile has a matching bridge 1.140 + * annotation in the bridge map 1.141 + */ 1.142 + protected void checkBridges(JavaFileObject jfo) { 1.143 + try { 1.144 + ClassFile cf = ClassFile.read(jfo.openInputStream()); 1.145 + System.err.println("checking: " + cf.getName()); 1.146 + 1.147 + List<Bridge> bridgeList = bridgesMap.get(cf.getName()); 1.148 + if (bridgeList == null) { 1.149 + //no bridges - nothing to check; 1.150 + bridgeList = List.nil(); 1.151 + } 1.152 + 1.153 + for (Method m : cf.methods) { 1.154 + if (m.access_flags.is(AccessFlags.ACC_SYNTHETIC | AccessFlags.ACC_BRIDGE)) { 1.155 + //this is a bridge - see if there's a match in the bridge list 1.156 + Bridge match = null; 1.157 + for (Bridge b : bridgeList) { 1.158 + if (b.value().equals(descriptor(m, cf.constant_pool))) { 1.159 + match = b; 1.160 + break; 1.161 + } 1.162 + } 1.163 + if (match == null) { 1.164 + error("No annotation for bridge method: " + descriptor(m, cf.constant_pool)); 1.165 + } else { 1.166 + bridgeList = drop(bridgeList, match); 1.167 + } 1.168 + } 1.169 + } 1.170 + if (bridgeList.nonEmpty()) { 1.171 + error("Redundant bridge annotation found: " + bridgeList.head.value()); 1.172 + } 1.173 + } catch (Exception e) { 1.174 + e.printStackTrace(); 1.175 + throw new Error("error reading " + jfo.toUri() +": " + e); 1.176 + } 1.177 + } 1.178 + 1.179 + /** 1.180 + * Log an error 1.181 + */ 1.182 + protected void error(String msg) { 1.183 + nerrors++; 1.184 + System.err.printf("Error occurred while checking file: %s\nreason: %s\n", jfo.getName(), msg); 1.185 + } 1.186 + 1.187 + /** 1.188 + * This annotation processor is used to populate the bridge map with the 1.189 + * contents of the annotations that are found on the tests being compiled 1.190 + */ 1.191 + @SupportedAnnotationTypes({"Bridges","Bridge"}) 1.192 + class BridgeFinder extends JavacTestingAbstractProcessor { 1.193 + @Override 1.194 + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1.195 + if (roundEnv.processingOver()) 1.196 + return true; 1.197 + 1.198 + TypeElement bridgeAnno = elements.getTypeElement("Bridge"); 1.199 + TypeElement bridgesAnno = elements.getTypeElement("Bridges"); 1.200 + 1.201 + //see if there are repeated annos 1.202 + for (Element elem: roundEnv.getElementsAnnotatedWith(bridgesAnno)) { 1.203 + List<Bridge> bridgeList = List.nil(); 1.204 + Bridges bridges = elem.getAnnotation(Bridges.class); 1.205 + for (Bridge bridge : bridges.value()) { 1.206 + bridgeList = bridgeList.prepend(bridge); 1.207 + } 1.208 + bridgesMap.put(((ClassSymbol)elem).flatname.toString(), bridgeList); 1.209 + } 1.210 + 1.211 + //see if there are non-repeated annos 1.212 + for (Element elem: roundEnv.getElementsAnnotatedWith(bridgeAnno)) { 1.213 + Bridge bridge = elem.getAnnotation(Bridge.class); 1.214 + bridgesMap.put(((ClassSymbol)elem).flatname.toString(), 1.215 + List.of(bridge)); 1.216 + } 1.217 + 1.218 + return true; 1.219 + } 1.220 + } 1.221 +}