1 /* 2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8013789 27 * @summary Compiler should emit bridges in interfaces 28 * @library /tools/javac/lib 29 * @modules jdk.compiler/com.sun.tools.classfile 30 * jdk.compiler/com.sun.tools.javac.code 31 * jdk.compiler/com.sun.tools.javac.util 32 * @build JavacTestingAbstractProcessor BridgeHarness 33 * @run main BridgeHarness 34 */ 35 36 import com.sun.source.util.JavacTask; 37 import com.sun.tools.classfile.AccessFlags; 38 import com.sun.tools.classfile.ClassFile; 39 import com.sun.tools.classfile.ConstantPool; 40 import com.sun.tools.classfile.ConstantPoolException; 41 import com.sun.tools.classfile.Method; 42 import com.sun.tools.javac.code.Symbol.ClassSymbol; 43 import com.sun.tools.javac.util.List; 44 45 import java.io.File; 46 import java.io.InputStream; 47 import java.util.Arrays; 48 import java.util.Collections; 49 import java.util.HashMap; 50 import java.util.Map; 51 import java.util.Set; 52 53 import javax.annotation.processing.RoundEnvironment; 54 import javax.annotation.processing.SupportedAnnotationTypes; 55 import javax.lang.model.element.Element; 56 import javax.lang.model.element.TypeElement; 57 import javax.tools.JavaCompiler; 58 import javax.tools.JavaFileObject; 59 import javax.tools.StandardJavaFileManager; 60 import javax.tools.ToolProvider; 61 62 import static javax.tools.StandardLocation.*; 63 64 public class BridgeHarness { 65 66 /** number of errors found (must be zero for the test to pass) */ 67 static int nerrors = 0; 68 69 /** the (shared) Java compiler used for compiling the tests */ 70 static final JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 71 72 /** the (shared) file manager used by the compiler */ 73 static final StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); 74 75 public static void main(String[] args) throws Exception { 76 try { 77 //set sourcepath 78 fm.setLocation(SOURCE_PATH, 79 Arrays.asList(new File(System.getProperty("test.src"), "tests"))); 80 //set output (-d) 81 fm.setLocation(javax.tools.StandardLocation.CLASS_OUTPUT, 82 Arrays.asList(new File(System.getProperty("user.dir")))); 83 for (JavaFileObject jfo : fm.list(SOURCE_PATH, "", Collections.singleton(JavaFileObject.Kind.SOURCE), true)) { 84 //for each source, compile and check against annotations 85 new BridgeHarness(jfo).compileAndCheck(); 86 } 87 //if there were errors, fail 88 if (nerrors > 0) { 89 throw new AssertionError("Errors were found"); 90 } 91 } finally { 92 fm.close(); 93 } 94 } 95 96 /* utility methods */ 97 98 /** 99 * Remove an element from a list 100 */ 101 static <Z> List<Z> drop(List<Z> lz, Z z) { 102 if (lz.head == z) { 103 return drop(lz.tail, z); 104 } else if (lz.isEmpty()) { 105 return lz; 106 } else { 107 return drop(lz.tail, z).prepend(lz.head); 108 } 109 } 110 111 /** 112 * return a string representation of a bytecode method 113 */ 114 static String descriptor(Method m, ConstantPool cp) throws ConstantPoolException { 115 return m.getName(cp) + m.descriptor.getValue(cp); 116 } 117 118 /* test harness */ 119 120 /** Test file to be compiled */ 121 JavaFileObject jfo; 122 123 /** Mapping between class name and list of bridges in class with that name */ 124 Map<String, List<Bridge>> bridgesMap = new HashMap<String, List<Bridge>>(); 125 126 protected BridgeHarness(JavaFileObject jfo) { 127 this.jfo = jfo; 128 } 129 130 /** 131 * Compile a test using a custom annotation processor and check the generated 132 * bytecode against discovered annotations. 133 */ 134 protected void compileAndCheck() throws Exception { 135 JavacTask ct = (JavacTask)comp.getTask(null, fm, null, null, null, Arrays.asList(jfo)); 136 ct.setProcessors(Collections.singleton(new BridgeFinder())); 137 138 for (JavaFileObject jfo : ct.generate()) { 139 checkBridges(jfo); 140 } 141 } 142 143 /** 144 * Check that every bridge in the generated classfile has a matching bridge 145 * annotation in the bridge map 146 */ 147 protected void checkBridges(JavaFileObject jfo) { 148 try (InputStream is = jfo.openInputStream()) { 149 ClassFile cf = ClassFile.read(is); 150 System.err.println("checking: " + cf.getName()); 151 152 List<Bridge> bridgeList = bridgesMap.get(cf.getName()); 153 if (bridgeList == null) { 154 //no bridges - nothing to check; 155 bridgeList = List.nil(); 156 } 157 158 for (Method m : cf.methods) { 159 if (m.access_flags.is(AccessFlags.ACC_SYNTHETIC | AccessFlags.ACC_BRIDGE)) { 160 //this is a bridge - see if there's a match in the bridge list 161 Bridge match = null; 162 for (Bridge b : bridgeList) { 163 if (b.value().equals(descriptor(m, cf.constant_pool))) { 164 match = b; 165 break; 166 } 167 } 168 if (match == null) { 169 error("No annotation for bridge method: " + descriptor(m, cf.constant_pool)); 170 } else { 171 bridgeList = drop(bridgeList, match); 172 } 173 } 174 } 175 if (bridgeList.nonEmpty()) { 176 error("Redundant bridge annotation found: " + bridgeList.head.value()); 177 } 178 } catch (Exception e) { 179 e.printStackTrace(); 180 throw new Error("error reading " + jfo.toUri() +": " + e); 181 } 182 } 183 184 /** 185 * Log an error 186 */ 187 protected void error(String msg) { 188 nerrors++; 189 System.err.printf("Error occurred while checking file: %s\nreason: %s\n", jfo.getName(), msg); 190 } 191 192 /** 193 * This annotation processor is used to populate the bridge map with the 194 * contents of the annotations that are found on the tests being compiled 195 */ 196 @SupportedAnnotationTypes({"Bridges","Bridge"}) 197 class BridgeFinder extends JavacTestingAbstractProcessor { 198 @Override 199 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 200 if (roundEnv.processingOver()) 201 return true; 202 203 TypeElement bridgeAnno = elements.getTypeElement("Bridge"); 204 TypeElement bridgesAnno = elements.getTypeElement("Bridges"); 205 206 //see if there are repeated annos 207 for (Element elem: roundEnv.getElementsAnnotatedWith(bridgesAnno)) { 208 List<Bridge> bridgeList = List.nil(); 209 Bridges bridges = elem.getAnnotation(Bridges.class); 210 for (Bridge bridge : bridges.value()) { 211 bridgeList = bridgeList.prepend(bridge); 212 } 213 bridgesMap.put(((ClassSymbol)elem).flatname.toString(), bridgeList); 214 } 215 216 //see if there are non-repeated annos 217 for (Element elem: roundEnv.getElementsAnnotatedWith(bridgeAnno)) { 218 Bridge bridge = elem.getAnnotation(Bridge.class); 219 bridgesMap.put(((ClassSymbol)elem).flatname.toString(), 220 List.of(bridge)); 221 } 222 223 return true; 224 } 225 } 226 }