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 }