001/* 002 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.graphbuilderconf; 024 025import java.lang.reflect.*; 026import java.util.*; 027import java.util.stream.*; 028 029import jdk.internal.jvmci.common.*; 030import jdk.internal.jvmci.meta.*; 031import sun.misc.*; 032 033import com.oracle.graal.nodes.*; 034 035/** 036 * An {@link InvocationPlugin} for a method where the implementation of the method is provided by a 037 * {@linkplain #getSubstitute(MetaAccessProvider) substitute} method. A substitute method must be 038 * static even if the substituted method is not. 039 */ 040public final class MethodSubstitutionPlugin implements InvocationPlugin { 041 042 private ResolvedJavaMethod cachedSubstitute; 043 044 /** 045 * The class in which the substitute method is declared. 046 */ 047 private final Class<?> declaringClass; 048 049 /** 050 * The name of the original and substitute method. 051 */ 052 private final String name; 053 054 /** 055 * The parameter types of the substitute method. 056 */ 057 private final Class<?>[] parameters; 058 059 private final boolean originalIsStatic; 060 061 /** 062 * Creates a method substitution plugin. 063 * 064 * @param declaringClass the class in which the substitute method is declared 065 * @param name the name of the substitute method 066 * @param parameters the parameter types of the substitute method. If the original method is not 067 * static, then {@code parameters[0]} must be the {@link Class} value denoting 068 * {@link InvocationPlugin.Receiver} 069 */ 070 public MethodSubstitutionPlugin(Class<?> declaringClass, String name, Class<?>... parameters) { 071 this.declaringClass = declaringClass; 072 this.name = name; 073 this.parameters = parameters; 074 this.originalIsStatic = parameters.length == 0 || parameters[0] != InvocationPlugin.Receiver.class; 075 } 076 077 /** 078 * Creates a method substitution plugin. 079 * 080 * @param declaringClass the class in which the substitute method is declared 081 * @param name the name of the substitute method 082 * @param parameters the parameter types of the substitute method 083 */ 084 public MethodSubstitutionPlugin(boolean originalIsStatic, Class<?> declaringClass, String name, Class<?>... parameters) { 085 this.declaringClass = declaringClass; 086 this.name = name; 087 this.parameters = parameters; 088 this.originalIsStatic = originalIsStatic; 089 } 090 091 public boolean inlineOnly() { 092 // Conservatively assume MacroNodes may be used in a substitution 093 return true; 094 } 095 096 /** 097 * Gets the substitute method, resolving it first if necessary. 098 */ 099 public ResolvedJavaMethod getSubstitute(MetaAccessProvider metaAccess) { 100 if (cachedSubstitute == null) { 101 cachedSubstitute = metaAccess.lookupJavaMethod(getJavaSubstitute()); 102 } 103 return cachedSubstitute; 104 } 105 106 /** 107 * Gets the reflection API version of the substitution method. 108 */ 109 Method getJavaSubstitute() throws JVMCIError { 110 Method substituteMethod = lookupSubstitute(); 111 int modifiers = substituteMethod.getModifiers(); 112 if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { 113 throw new JVMCIError("Substitution method must not be abstract or native: " + substituteMethod); 114 } 115 if (!Modifier.isStatic(modifiers)) { 116 throw new JVMCIError("Substitution method must be static: " + substituteMethod); 117 } 118 return substituteMethod; 119 } 120 121 /** 122 * Determines if a given method is the substitute method of this plugin. 123 */ 124 private boolean isSubstitute(Method m) { 125 if (Modifier.isStatic(m.getModifiers()) && m.getName().equals(name)) { 126 if (parameters.length == m.getParameterCount()) { 127 Class<?>[] mparams = m.getParameterTypes(); 128 int start = 0; 129 if (!originalIsStatic) { 130 start = 1; 131 if (!mparams[0].isAssignableFrom(parameters[0])) { 132 return false; 133 } 134 } 135 for (int i = start; i < mparams.length; i++) { 136 if (mparams[i] != parameters[i]) { 137 return false; 138 } 139 } 140 } 141 return true; 142 } 143 return false; 144 } 145 146 /** 147 * Gets the substitute method of this plugin. 148 */ 149 private Method lookupSubstitute() { 150 for (Method m : declaringClass.getDeclaredMethods()) { 151 if (isSubstitute(m)) { 152 return m; 153 } 154 } 155 throw new JVMCIError("No method found specified by %s", this); 156 } 157 158 /** 159 * Resolves a name to a class. 160 * 161 * @param className the name of the class to resolve 162 * @param optional if true, resolution failure returns null 163 * @return the resolved class or null if resolution fails and {@code optional} is true 164 */ 165 public static Class<?> resolveClass(String className, boolean optional) { 166 try { 167 // Need to use launcher class path to handle classes 168 // that are not on the boot class path 169 ClassLoader cl = Launcher.getLauncher().getClassLoader(); 170 return Class.forName(className, false, cl); 171 } catch (ClassNotFoundException e) { 172 if (optional) { 173 return null; 174 } 175 throw new JVMCIError("Could not resolve type " + className); 176 } 177 } 178 179 @Override 180 public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) { 181 ResolvedJavaMethod subst = getSubstitute(b.getMetaAccess()); 182 if (receiver != null) { 183 receiver.get(); 184 } 185 b.intrinsify(targetMethod, subst, argsIncludingReceiver); 186 return true; 187 } 188 189 public StackTraceElement getApplySourceLocation(MetaAccessProvider metaAccess) { 190 Class<?> c = getClass(); 191 for (Method m : c.getDeclaredMethods()) { 192 if (m.getName().equals("execute")) { 193 return metaAccess.lookupJavaMethod(m).asStackTraceElement(0); 194 } 195 } 196 throw new JVMCIError("could not find method named \"execute\" in " + c.getName()); 197 } 198 199 @Override 200 public String toString() { 201 return String.format("%s[%s.%s(%s)]", getClass().getSimpleName(), declaringClass.getName(), name, 202 Arrays.asList(parameters).stream().map(c -> c.getSimpleName()).collect(Collectors.joining(", "))); 203 } 204}