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 }