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