/* * Copyright (c) 2016, 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 6695379 * @see 495396 * @see 427745 */ public class BridgeMethodUtils { /** * 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("") && 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 {@link ResolvedJavaMethod#getAnnotation(Class)} that handles the absence of * annotations on bridge methods where the bridged method has annotations. */ public static T getAnnotation(Class 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 {@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 {@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 {@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 {@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 getParameterAnnotation(Class 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; } }