1 /* 2 * Copyright (c) 2016, 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 package org.graalvm.compiler.bytecode; 26 27 import static org.graalvm.compiler.bytecode.Bytecodes.ATHROW; 28 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEINTERFACE; 29 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESPECIAL; 30 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESTATIC; 31 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEVIRTUAL; 32 33 import java.lang.annotation.Annotation; 34 35 import jdk.vm.ci.meta.ConstantPool; 36 import jdk.vm.ci.meta.ResolvedJavaMethod; 37 38 /** 39 * Utilities for working around the absence of method annotations and parameter annotations on 40 * bridge methods where the bridged methods have method annotations or parameter annotations. Not 41 * all Java compilers copy method annotations and parameter annotations to bridge methods. 42 * 43 * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6695379">6695379</a> 44 * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=495396">495396</a> 45 * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=427745">427745</a> 46 */ 47 public class BridgeMethodUtils { 48 49 /** 50 * Gets the method bridged to by a {@linkplain ResolvedJavaMethod#isBridge() bridge} method. The 51 * value returned is the method called by {@code method} that has the same name as 52 * {@code bridge}. 53 * 54 * @param bridge a bridge method 55 * @return the method called by {@code bridge} whose name is the same as 56 * {@code bridge.getName()} 57 */ 58 public static ResolvedJavaMethod getBridgedMethod(ResolvedJavaMethod bridge) { 59 assert bridge.isBridge(); 60 Bytecode code = new ResolvedJavaMethodBytecode(bridge); 61 BytecodeStream stream = new BytecodeStream(code.getCode()); 62 int opcode = stream.currentBC(); 63 ResolvedJavaMethod bridged = null; 64 boolean calledAbstractMethodErrorConstructor = false; 65 while (opcode != Bytecodes.END) { 66 switch (opcode) { 67 case INVOKEVIRTUAL: 68 case INVOKESPECIAL: 69 case INVOKESTATIC: 70 case INVOKEINTERFACE: { 71 int cpi = stream.readCPI(); 72 ConstantPool cp = code.getConstantPool(); 73 cp.loadReferencedType(cpi, opcode); 74 ResolvedJavaMethod method = (ResolvedJavaMethod) cp.lookupMethod(cpi, opcode); 75 if (method.getName().equals(bridge.getName())) { 76 if (!assertionsEnabled()) { 77 return method; 78 } 79 assert bridged == null || bridged.equals(method) : String.format("Found calls to different methods named %s in bridge method %s%n callee 1: %s%n callee 2: %s", 80 bridge.getName(), bridge.format("%R %H.%n(%P)"), bridged.format("%R %H.%n(%P)"), method.format("%R %H.%n(%P)")); 81 bridged = method; 82 } else if (method.getName().equals("<init>") && method.getDeclaringClass().getName().equals("Ljava/lang/AbstractMethodError;")) { 83 calledAbstractMethodErrorConstructor = true; 84 } 85 break; 86 } 87 case ATHROW: { 88 if (calledAbstractMethodErrorConstructor) { 89 // This is a miranda method 90 return null; 91 } 92 } 93 } 94 stream.next(); 95 opcode = stream.currentBC(); 96 } 97 if (bridged == null) { 98 String dis = new BytecodeDisassembler().disassemble(bridge); 99 throw new InternalError(String.format("Couldn't find method bridged by %s:%n%s", bridge.format("%R %H.%n(%P)"), dis)); 100 } 101 return bridged; 102 } 103 104 @SuppressWarnings("all") 105 private static boolean assertionsEnabled() { 106 boolean enabled = false; 107 assert enabled = true; 108 return enabled; 109 } 110 111 /** 112 * A helper for {@link ResolvedJavaMethod#getAnnotation(Class)} that handles the absence of 113 * annotations on bridge methods where the bridged method has annotations. 114 */ 115 public static <T extends Annotation> T getAnnotation(Class<T> annotationClass, ResolvedJavaMethod method) { 116 T a = method.getAnnotation(annotationClass); 117 if (a == null && method.isBridge()) { 118 ResolvedJavaMethod bridged = getBridgedMethod(method); 119 if (bridged != null) { 120 a = bridged.getAnnotation(annotationClass); 121 } 122 } 123 return a; 124 } 125 126 /** 127 * A helper for {@link ResolvedJavaMethod#getAnnotations()} that handles the absence of 128 * annotations on bridge methods where the bridged method has annotations. 129 */ 130 public static Annotation[] getAnnotations(ResolvedJavaMethod method) { 131 Annotation[] a = method.getAnnotations(); 132 if (a.length == 0 && method.isBridge()) { 133 ResolvedJavaMethod bridged = getBridgedMethod(method); 134 if (bridged != null) { 135 a = bridged.getAnnotations(); 136 } 137 } 138 return a; 139 } 140 141 /** 142 * A helper for {@link ResolvedJavaMethod#getDeclaredAnnotations()} that handles the absence of 143 * annotations on bridge methods where the bridged method has annotations. 144 */ 145 public static Annotation[] getDeclaredAnnotations(ResolvedJavaMethod method) { 146 Annotation[] a = method.getAnnotations(); 147 if (a.length == 0 && method.isBridge()) { 148 ResolvedJavaMethod bridged = getBridgedMethod(method); 149 if (bridged != null) { 150 a = bridged.getDeclaredAnnotations(); 151 } 152 } 153 return a; 154 } 155 156 /** 157 * A helper for {@link ResolvedJavaMethod#getParameterAnnotations()} that handles the absence of 158 * parameter annotations on bridge methods where the bridged method has parameter annotations. 159 */ 160 public static Annotation[][] getParameterAnnotations(ResolvedJavaMethod method) { 161 Annotation[][] a = method.getParameterAnnotations(); 162 if (a.length == 0 && method.isBridge()) { 163 ResolvedJavaMethod bridged = getBridgedMethod(method); 164 if (bridged != null) { 165 a = bridged.getParameterAnnotations(); 166 } 167 } 168 return a; 169 } 170 171 /** 172 * A helper for {@link ResolvedJavaMethod#getParameterAnnotation(Class, int)} that handles the 173 * absence of parameter annotations on bridge methods where the bridged method has parameter 174 * annotations. 175 */ 176 public static <T extends Annotation> T getParameterAnnotation(Class<T> annotationClass, int parameterIndex, ResolvedJavaMethod method) { 177 T a = method.getParameterAnnotation(annotationClass, parameterIndex); 178 if (a == null && method.isBridge()) { 179 ResolvedJavaMethod bridged = getBridgedMethod(method); 180 if (bridged != null) { 181 a = bridged.getParameterAnnotation(annotationClass, parameterIndex); 182 } 183 } 184 return a; 185 } 186 }