/*
 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package org.graalvm.compiler.bytecode;

import static org.graalvm.compiler.bytecode.Bytecodes.ATHROW;
import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEINTERFACE;
import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESPECIAL;
import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESTATIC;
import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEVIRTUAL;

import java.lang.annotation.Annotation;

import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.ResolvedJavaMethod;

Utilities for working around the absence of method annotations and parameter annotations on bridge methods where the bridged methods have method annotations or parameter annotations. Not all Java compilers copy method annotations and parameter annotations to bridge methods.
See Also:
/** * Utilities for working around the absence of method annotations and parameter annotations on * bridge methods where the bridged methods have method annotations or parameter annotations. Not * all Java compilers copy method annotations and parameter annotations to bridge methods. * * @see <a href="http://bugs.java.com/view_bug.do?bug_id=6695379">6695379</a> * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=495396">495396</a> * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=427745">427745</a> */
public class BridgeMethodUtils {
Gets the method bridged to by a bridge method. The value returned is the method called by method that has the same name as bridge.
Params:
  • bridge – a bridge method
Returns:the method called by bridge whose name is the same as bridge.getName()
/** * Gets the method bridged to by a {@linkplain ResolvedJavaMethod#isBridge() bridge} method. The * value returned is the method called by {@code method} that has the same name as * {@code bridge}. * * @param bridge a bridge method * @return the method called by {@code bridge} whose name is the same as * {@code bridge.getName()} */
public static ResolvedJavaMethod getBridgedMethod(ResolvedJavaMethod bridge) { assert bridge.isBridge(); Bytecode code = new ResolvedJavaMethodBytecode(bridge); BytecodeStream stream = new BytecodeStream(code.getCode()); int opcode = stream.currentBC(); ResolvedJavaMethod bridged = null; boolean calledAbstractMethodErrorConstructor = false; while (opcode != Bytecodes.END) { switch (opcode) { case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: case INVOKEINTERFACE: { int cpi = stream.readCPI(); ConstantPool cp = code.getConstantPool(); cp.loadReferencedType(cpi, opcode); ResolvedJavaMethod method = (ResolvedJavaMethod) cp.lookupMethod(cpi, opcode); if (method.getName().equals(bridge.getName())) { if (!assertionsEnabled()) { return method; } 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", bridge.getName(), bridge.format("%R %H.%n(%P)"), bridged.format("%R %H.%n(%P)"), method.format("%R %H.%n(%P)")); bridged = method; } else if (method.getName().equals("<init>") && method.getDeclaringClass().getName().equals("Ljava/lang/AbstractMethodError;")) { calledAbstractMethodErrorConstructor = true; } break; } case ATHROW: { if (calledAbstractMethodErrorConstructor) { // This is a miranda method return null; } } } stream.next(); opcode = stream.currentBC(); } if (bridged == null) { String dis = new BytecodeDisassembler().disassemble(bridge); throw new InternalError(String.format("Couldn't find method bridged by %s:%n%s", bridge.format("%R %H.%n(%P)"), dis)); } return bridged; } @SuppressWarnings("all") private static boolean assertionsEnabled() { boolean enabled = false; assert enabled = true; return enabled; }
A helper for AnnotatedElement.getAnnotation(Class<Annotation>) that handles the absence of annotations on bridge methods where the bridged method has annotations.
/** * A helper for {@link ResolvedJavaMethod#getAnnotation(Class)} that handles the absence of * annotations on bridge methods where the bridged method has annotations. */
public static <T extends Annotation> T getAnnotation(Class<T> annotationClass, ResolvedJavaMethod method) { T a = method.getAnnotation(annotationClass); if (a == null && method.isBridge()) { ResolvedJavaMethod bridged = getBridgedMethod(method); if (bridged != null) { a = bridged.getAnnotation(annotationClass); } } return a; }
A helper for AnnotatedElement.getAnnotations() that handles the absence of annotations on bridge methods where the bridged method has annotations.
/** * A helper for {@link ResolvedJavaMethod#getAnnotations()} that handles the absence of * annotations on bridge methods where the bridged method has annotations. */
public static Annotation[] getAnnotations(ResolvedJavaMethod method) { Annotation[] a = method.getAnnotations(); if (a.length == 0 && method.isBridge()) { ResolvedJavaMethod bridged = getBridgedMethod(method); if (bridged != null) { a = bridged.getAnnotations(); } } return a; }
A helper for AnnotatedElement.getDeclaredAnnotations() that handles the absence of annotations on bridge methods where the bridged method has annotations.
/** * A helper for {@link ResolvedJavaMethod#getDeclaredAnnotations()} that handles the absence of * annotations on bridge methods where the bridged method has annotations. */
public static Annotation[] getDeclaredAnnotations(ResolvedJavaMethod method) { Annotation[] a = method.getAnnotations(); if (a.length == 0 && method.isBridge()) { ResolvedJavaMethod bridged = getBridgedMethod(method); if (bridged != null) { a = bridged.getDeclaredAnnotations(); } } return a; }
A helper for ResolvedJavaMethod.getParameterAnnotations() that handles the absence of parameter annotations on bridge methods where the bridged method has parameter annotations.
/** * A helper for {@link ResolvedJavaMethod#getParameterAnnotations()} that handles the absence of * parameter annotations on bridge methods where the bridged method has parameter annotations. */
public static Annotation[][] getParameterAnnotations(ResolvedJavaMethod method) { Annotation[][] a = method.getParameterAnnotations(); if (a.length == 0 && method.isBridge()) { ResolvedJavaMethod bridged = getBridgedMethod(method); if (bridged != null) { a = bridged.getParameterAnnotations(); } } return a; }
A helper for ResolvedJavaMethod.getParameterAnnotation(Class<Annotation>, int) that handles the absence of parameter annotations on bridge methods where the bridged method has parameter annotations.
/** * A helper for {@link ResolvedJavaMethod#getParameterAnnotation(Class, int)} that handles the * absence of parameter annotations on bridge methods where the bridged method has parameter * annotations. */
public static <T extends Annotation> T getParameterAnnotation(Class<T> annotationClass, int parameterIndex, ResolvedJavaMethod method) { T a = method.getParameterAnnotation(annotationClass, parameterIndex); if (a == null && method.isBridge()) { ResolvedJavaMethod bridged = getBridgedMethod(method); if (bridged != null) { a = bridged.getParameterAnnotation(annotationClass, parameterIndex); } } return a; } }