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 static java.lang.String.*; 026 027import java.lang.reflect.*; 028import java.util.*; 029 030import jdk.internal.jvmci.meta.*; 031import jdk.internal.jvmci.meta.MethodIdMap.*; 032 033import com.oracle.graal.graph.*; 034import com.oracle.graal.graph.iterators.*; 035import com.oracle.graal.nodes.*; 036 037/** 038 * Manages a set of {@link InvocationPlugin}s. 039 */ 040public class InvocationPlugins { 041 042 public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { 043 private final GraphBuilderContext parser; 044 private ValueNode[] args; 045 private ValueNode value; 046 047 public InvocationPluginReceiver(GraphBuilderContext parser) { 048 this.parser = parser; 049 } 050 051 @Override 052 public ValueNode get() { 053 assert args != null : "Cannot get the receiver of a static method"; 054 if (value == null) { 055 value = parser.nullCheckedValue(args[0]); 056 if (value != args[0]) { 057 args[0] = value; 058 } 059 } 060 return value; 061 } 062 063 @Override 064 public boolean isConstant() { 065 return args[0].isConstant(); 066 } 067 068 public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) { 069 if (!targetMethod.isStatic()) { 070 this.args = newArgs; 071 this.value = null; 072 return this; 073 } 074 return null; 075 } 076 } 077 078 /** 079 * Utility for 080 * {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...) 081 * registration} of invocation plugins. 082 */ 083 public static class Registration { 084 085 private final InvocationPlugins plugins; 086 private final Class<?> declaringClass; 087 088 /** 089 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 090 * given class. 091 * 092 * @param plugins where to register the plugins 093 * @param declaringClass the class declaring the methods for which plugins will be 094 * registered via this object 095 */ 096 public Registration(InvocationPlugins plugins, Class<?> declaringClass) { 097 this.plugins = plugins; 098 this.declaringClass = declaringClass; 099 } 100 101 /** 102 * Registers a plugin for a method with no arguments. 103 * 104 * @param name the name of the method 105 * @param plugin the plugin to be registered 106 */ 107 public void register0(String name, InvocationPlugin plugin) { 108 plugins.register(plugin, declaringClass, name); 109 } 110 111 /** 112 * Registers a plugin for a method with 1 argument. 113 * 114 * @param name the name of the method 115 * @param plugin the plugin to be registered 116 */ 117 public void register1(String name, Class<?> arg, InvocationPlugin plugin) { 118 plugins.register(plugin, declaringClass, name, arg); 119 } 120 121 /** 122 * Registers a plugin for a method with 2 arguments. 123 * 124 * @param name the name of the method 125 * @param plugin the plugin to be registered 126 */ 127 public void register2(String name, Class<?> arg1, Class<?> arg2, InvocationPlugin plugin) { 128 plugins.register(plugin, declaringClass, name, arg1, arg2); 129 } 130 131 /** 132 * Registers a plugin for a method with 3 arguments. 133 * 134 * @param name the name of the method 135 * @param plugin the plugin to be registered 136 */ 137 public void register3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) { 138 plugins.register(plugin, declaringClass, name, arg1, arg2, arg3); 139 } 140 141 /** 142 * Registers a plugin for a method with 4 arguments. 143 * 144 * @param name the name of the method 145 * @param plugin the plugin to be registered 146 */ 147 public void register4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) { 148 plugins.register(plugin, declaringClass, name, arg1, arg2, arg3, arg4); 149 } 150 151 /** 152 * Registers a plugin for a method with 5 arguments. 153 * 154 * @param name the name of the method 155 * @param plugin the plugin to be registered 156 */ 157 public void register5(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, Class<?> arg5, InvocationPlugin plugin) { 158 plugins.register(plugin, declaringClass, name, arg1, arg2, arg3, arg4, arg5); 159 } 160 161 /** 162 * Registers a plugin for an optional method with 3 arguments. 163 * 164 * @param name the name of the method 165 * @param plugin the plugin to be registered 166 */ 167 public void registerOptional3(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, InvocationPlugin plugin) { 168 plugins.registerOptional(plugin, declaringClass, name, arg1, arg2, arg3); 169 } 170 171 /** 172 * Registers a plugin for an optional method with 4 arguments. 173 * 174 * @param name the name of the method 175 * @param plugin the plugin to be registered 176 */ 177 public void registerOptional4(String name, Class<?> arg1, Class<?> arg2, Class<?> arg3, Class<?> arg4, InvocationPlugin plugin) { 178 plugins.registerOptional(plugin, declaringClass, name, arg1, arg2, arg3, arg4); 179 } 180 181 /** 182 * Registers a plugin that implements a method based on the bytecode of a substitute method. 183 * 184 * @param substituteDeclaringClass the class declaring the substitute method 185 * @param name the name of both the original and substitute method 186 * @param argumentTypes the argument types of the method. Element 0 of this array must be 187 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 188 * is non-static. Upon returning, element 0 will have been rewritten to 189 * {@code declaringClass} 190 */ 191 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Class<?>... argumentTypes) { 192 MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(substituteDeclaringClass, name, argumentTypes); 193 plugins.register(plugin, declaringClass, name, argumentTypes); 194 } 195 } 196 197 protected final MethodIdMap<InvocationPlugin> plugins; 198 199 /** 200 * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in 201 * this object. 202 */ 203 protected final InvocationPlugins parent; 204 205 private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { 206 this.plugins = new MethodIdMap<>(metaAccess); 207 InvocationPlugins p = parent; 208 // Only adopt a non-empty parent 209 while (p != null && p.size() == 0) { 210 p = p.parent; 211 } 212 this.parent = p; 213 } 214 215 /** 216 * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. 217 */ 218 public InvocationPlugins(InvocationPlugins parent) { 219 this(parent, parent.plugins.getMetaAccess()); 220 } 221 222 public InvocationPlugins(MetaAccessProvider metaAccess) { 223 this(null, metaAccess); 224 } 225 226 private void register(InvocationPlugin plugin, boolean isOptional, Class<?> declaringClass, String name, Class<?>... argumentTypes) { 227 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; 228 if (!isStatic) { 229 argumentTypes[0] = declaringClass; 230 } 231 MethodKey<InvocationPlugin> methodInfo = plugins.put(plugin, isStatic, isOptional, declaringClass, name, argumentTypes); 232 assert Checker.check(this, methodInfo, plugin); 233 } 234 235 /** 236 * Registers an invocation plugin for a given method. There must be no plugin currently 237 * registered for {@code method}. 238 * 239 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 240 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 241 * non-static. Upon returning, element 0 will have been rewritten to 242 * {@code declaringClass} 243 */ 244 public void register(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) { 245 register(plugin, false, declaringClass, name, argumentTypes); 246 } 247 248 /** 249 * Registers an invocation plugin for a given, optional method. There must be no plugin 250 * currently registered for {@code method}. 251 * 252 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 253 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 254 * non-static. Upon returning, element 0 will have been rewritten to 255 * {@code declaringClass} 256 */ 257 public void registerOptional(InvocationPlugin plugin, Class<?> declaringClass, String name, Class<?>... argumentTypes) { 258 register(plugin, true, declaringClass, name, argumentTypes); 259 } 260 261 /** 262 * Gets the plugin for a given method. 263 * 264 * @param method the method to lookup 265 * @return the plugin associated with {@code method} or {@code null} if none exists 266 */ 267 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { 268 assert method instanceof MethodIdHolder; 269 if (parent != null) { 270 InvocationPlugin plugin = parent.lookupInvocation(method); 271 if (plugin != null) { 272 return plugin; 273 } 274 } 275 return plugins.get((MethodIdHolder) method); 276 } 277 278 /** 279 * Disallows new registrations of new plugins, and creates the internal tables for method 280 * lookup. 281 */ 282 public void closeRegistration() { 283 plugins.createEntries(); 284 } 285 286 /** 287 * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} 288 * before searching in this object. 289 */ 290 public InvocationPlugins getParent() { 291 return parent; 292 } 293 294 @Override 295 public String toString() { 296 return plugins + " / parent: " + this.parent; 297 } 298 299 private static class Checker { 300 private static final int MAX_ARITY = 5; 301 /** 302 * The set of all {@link InvocationPlugin#apply} method signatures. 303 */ 304 static final Class<?>[][] SIGS; 305 306 static { 307 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY); 308 for (Method method : InvocationPlugin.class.getDeclaredMethods()) { 309 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) { 310 Class<?>[] sig = method.getParameterTypes(); 311 assert sig[0] == GraphBuilderContext.class; 312 assert sig[1] == ResolvedJavaMethod.class; 313 assert sig[2] == InvocationPlugin.Receiver.class; 314 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class); 315 while (sigs.size() < sig.length - 2) { 316 sigs.add(null); 317 } 318 sigs.set(sig.length - 3, sig); 319 } 320 } 321 assert sigs.indexOf(null) == -1 : format("need to add an apply() method to %s that takes %d %s arguments ", InvocationPlugin.class.getName(), sigs.indexOf(null), 322 ValueNode.class.getSimpleName()); 323 SIGS = sigs.toArray(new Class<?>[sigs.size()][]); 324 } 325 326 public static boolean check(InvocationPlugins plugins, MethodKey<InvocationPlugin> method, InvocationPlugin plugin) { 327 InvocationPlugins p = plugins.parent; 328 while (p != null) { 329 assert !p.plugins.containsKey(method) : "a plugin is already registered for " + method; 330 p = p.parent; 331 } 332 if (plugin instanceof ForeignCallPlugin) { 333 return true; 334 } 335 if (plugin instanceof MethodSubstitutionPlugin) { 336 MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; 337 msplugin.getJavaSubstitute(); 338 return true; 339 } 340 int arguments = method.getDeclaredParameterCount(); 341 assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method); 342 for (Method m : plugin.getClass().getDeclaredMethods()) { 343 if (m.getName().equals("apply")) { 344 Class<?>[] parameterTypes = m.getParameterTypes(); 345 if (Arrays.equals(SIGS[arguments], parameterTypes)) { 346 return true; 347 } 348 } 349 } 350 throw new AssertionError(format("graph builder plugin for %s not found", method)); 351 } 352 } 353 354 public int size() { 355 return plugins.size(); 356 } 357 358 /** 359 * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. 360 * 361 * @param b the graph builder that applied the plugin 362 * @param plugin a plugin that was just applied 363 * @param newNodes the nodes added to the graph by {@code plugin} 364 * @throws AssertionError if any check fail 365 */ 366 public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) { 367 if (parent != null) { 368 parent.checkNewNodes(b, plugin, newNodes); 369 } 370 } 371}