1 /* 2 * Copyright (c) 2015, 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 package org.graalvm.compiler.nodes.graphbuilderconf; 24 25 import static java.lang.String.format; 26 27 import java.lang.reflect.Executable; 28 import java.lang.reflect.Method; 29 import java.lang.reflect.Modifier; 30 import java.lang.reflect.Type; 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.List; 34 import java.util.Map; 35 import org.graalvm.compiler.api.replacements.MethodSubstitution; 36 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry; 37 import org.graalvm.compiler.bytecode.BytecodeProvider; 38 import org.graalvm.compiler.debug.GraalError; 39 import org.graalvm.compiler.graph.Node; 40 import org.graalvm.compiler.graph.iterators.NodeIterable; 41 import org.graalvm.compiler.nodes.ValueNode; 42 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; 43 import org.graalvm.util.Equivalence; 44 import org.graalvm.util.EconomicMap; 45 import org.graalvm.util.EconomicSet; 46 import org.graalvm.util.UnmodifiableMapCursor; 47 48 import jdk.vm.ci.meta.MetaAccessProvider; 49 import jdk.vm.ci.meta.MetaUtil; 50 import jdk.vm.ci.meta.ResolvedJavaMethod; 51 52 /** 53 * Manages a set of {@link InvocationPlugin}s. 54 */ 55 public class InvocationPlugins { 56 57 public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { 58 private final GraphBuilderContext parser; 59 private ValueNode[] args; 60 private ValueNode value; 61 62 public InvocationPluginReceiver(GraphBuilderContext parser) { 63 this.parser = parser; 64 } 65 66 @Override 67 public ValueNode get(boolean performNullCheck) { 68 assert args != null : "Cannot get the receiver of a static method"; 69 if (!performNullCheck) { 70 return args[0]; 71 } 72 if (value == null) { 73 value = parser.nullCheckedValue(args[0]); 74 if (value != args[0]) { 75 args[0] = value; 76 } 77 } 78 return value; 79 } 80 81 @Override 82 public boolean isConstant() { 83 return args[0].isConstant(); 84 } 85 86 public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) { 87 if (!targetMethod.isStatic()) { 88 this.args = newArgs; 89 this.value = null; 90 return this; 91 } 92 return null; 93 } 94 } 95 96 /** 97 * A symbol that is lazily {@linkplain OptionalLazySymbol#resolve() resolved} to a {@link Type}. 98 */ 99 static class OptionalLazySymbol implements Type { 100 private static final Class<?> MASK_NULL = OptionalLazySymbol.class; 101 private final String name; 102 private Class<?> resolved; 103 104 OptionalLazySymbol(String name) { 105 this.name = name; 106 } 107 108 @Override 109 public String getTypeName() { 110 return name; 111 } 112 113 /** 114 * Gets the resolved {@link Class} corresponding to this symbol or {@code null} if 115 * resolution fails. 116 */ 117 public Class<?> resolve() { 118 if (resolved == null) { 119 Class<?> resolvedOrNull = resolveClass(name, true); 120 resolved = resolvedOrNull == null ? MASK_NULL : resolvedOrNull; 121 } 122 return resolved == MASK_NULL ? null : resolved; 123 } 124 125 @Override 126 public String toString() { 127 return name; 128 } 129 } 130 131 /** 132 * Utility for {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...) 133 * registration} of invocation plugins. 134 */ 135 public static class Registration implements MethodSubstitutionRegistry { 136 137 private final InvocationPlugins plugins; 138 private final Type declaringType; 139 private final BytecodeProvider methodSubstitutionBytecodeProvider; 140 private boolean allowOverwrite; 141 142 @Override 143 public Class<?> getReceiverType() { 144 return Receiver.class; 145 } 146 147 /** 148 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 149 * given class. 150 * 151 * @param plugins where to register the plugins 152 * @param declaringType the class declaring the methods for which plugins will be registered 153 * via this object 154 */ 155 public Registration(InvocationPlugins plugins, Type declaringType) { 156 this.plugins = plugins; 157 this.declaringType = declaringType; 158 this.methodSubstitutionBytecodeProvider = null; 159 } 160 161 /** 162 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 163 * given class. 164 * 165 * @param plugins where to register the plugins 166 * @param declaringType the class declaring the methods for which plugins will be registered 167 * via this object 168 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for 169 * method substitutions 170 */ 171 public Registration(InvocationPlugins plugins, Type declaringType, BytecodeProvider methodSubstitutionBytecodeProvider) { 172 this.plugins = plugins; 173 this.declaringType = declaringType; 174 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider; 175 } 176 177 /** 178 * Creates an object for registering {@link InvocationPlugin}s for methods declared by a 179 * given class. 180 * 181 * @param plugins where to register the plugins 182 * @param declaringClassName the name of the class class declaring the methods for which 183 * plugins will be registered via this object 184 * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for 185 * method substitutions 186 */ 187 public Registration(InvocationPlugins plugins, String declaringClassName, BytecodeProvider methodSubstitutionBytecodeProvider) { 188 this.plugins = plugins; 189 this.declaringType = new OptionalLazySymbol(declaringClassName); 190 this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider; 191 } 192 193 /** 194 * Configures this registration to allow or disallow overwriting of invocation plugins. 195 */ 196 public Registration setAllowOverwrite(boolean allowOverwrite) { 197 this.allowOverwrite = allowOverwrite; 198 return this; 199 } 200 201 /** 202 * Registers a plugin for a method with no arguments. 203 * 204 * @param name the name of the method 205 * @param plugin the plugin to be registered 206 */ 207 public void register0(String name, InvocationPlugin plugin) { 208 plugins.register(plugin, false, allowOverwrite, declaringType, name); 209 } 210 211 /** 212 * Registers a plugin for a method with 1 argument. 213 * 214 * @param name the name of the method 215 * @param plugin the plugin to be registered 216 */ 217 public void register1(String name, Type arg, InvocationPlugin plugin) { 218 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg); 219 } 220 221 /** 222 * Registers a plugin for a method with 2 arguments. 223 * 224 * @param name the name of the method 225 * @param plugin the plugin to be registered 226 */ 227 public void register2(String name, Type arg1, Type arg2, InvocationPlugin plugin) { 228 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2); 229 } 230 231 /** 232 * Registers a plugin for a method with 3 arguments. 233 * 234 * @param name the name of the method 235 * @param plugin the plugin to be registered 236 */ 237 public void register3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) { 238 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3); 239 } 240 241 /** 242 * Registers a plugin for a method with 4 arguments. 243 * 244 * @param name the name of the method 245 * @param plugin the plugin to be registered 246 */ 247 public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) { 248 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4); 249 } 250 251 /** 252 * Registers a plugin for a method with 5 arguments. 253 * 254 * @param name the name of the method 255 * @param plugin the plugin to be registered 256 */ 257 public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) { 258 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5); 259 } 260 261 /** 262 * Registers a plugin for a method with 6 arguments. 263 * 264 * @param name the name of the method 265 * @param plugin the plugin to be registered 266 */ 267 public void register6(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, InvocationPlugin plugin) { 268 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6); 269 } 270 271 /** 272 * Registers a plugin for a method with 7 arguments. 273 * 274 * @param name the name of the method 275 * @param plugin the plugin to be registered 276 */ 277 public void register7(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7, InvocationPlugin plugin) { 278 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 279 } 280 281 /** 282 * Registers a plugin for an optional method with no arguments. 283 * 284 * @param name the name of the method 285 * @param plugin the plugin to be registered 286 */ 287 public void registerOptional0(String name, InvocationPlugin plugin) { 288 plugins.register(plugin, true, allowOverwrite, declaringType, name); 289 } 290 291 /** 292 * Registers a plugin for an optional method with 1 argument. 293 * 294 * @param name the name of the method 295 * @param plugin the plugin to be registered 296 */ 297 public void registerOptional1(String name, Type arg, InvocationPlugin plugin) { 298 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg); 299 } 300 301 /** 302 * Registers a plugin for an optional method with 2 arguments. 303 * 304 * @param name the name of the method 305 * @param plugin the plugin to be registered 306 */ 307 public void registerOptional2(String name, Type arg1, Type arg2, InvocationPlugin plugin) { 308 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2); 309 } 310 311 /** 312 * Registers a plugin for an optional method with 3 arguments. 313 * 314 * @param name the name of the method 315 * @param plugin the plugin to be registered 316 */ 317 public void registerOptional3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) { 318 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3); 319 } 320 321 /** 322 * Registers a plugin for an optional method with 4 arguments. 323 * 324 * @param name the name of the method 325 * @param plugin the plugin to be registered 326 */ 327 public void registerOptional4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) { 328 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4); 329 } 330 331 /** 332 * Registers a plugin that implements a method based on the bytecode of a substitute method. 333 * 334 * @param substituteDeclaringClass the class declaring the substitute method 335 * @param name the name of both the original and substitute method 336 * @param argumentTypes the argument types of the method. Element 0 of this array must be 337 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 338 * is non-static. Upon returning, element 0 will have been rewritten to 339 * {@code declaringClass} 340 */ 341 @Override 342 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) { 343 registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes); 344 } 345 346 /** 347 * Registers a plugin that implements a method based on the bytecode of a substitute method. 348 * 349 * @param substituteDeclaringClass the class declaring the substitute method 350 * @param name the name of both the original method 351 * @param substituteName the name of the substitute method 352 * @param argumentTypes the argument types of the method. Element 0 of this array must be 353 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 354 * is non-static. Upon returning, element 0 will have been rewritten to 355 * {@code declaringClass} 356 */ 357 @Override 358 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) { 359 MethodSubstitutionPlugin plugin = createMethodSubstitution(substituteDeclaringClass, substituteName, argumentTypes); 360 plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes); 361 } 362 363 public MethodSubstitutionPlugin createMethodSubstitution(Class<?> substituteDeclaringClass, String substituteName, Type... argumentTypes) { 364 assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider"; 365 MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes); 366 return plugin; 367 } 368 369 } 370 371 /** 372 * Key for a {@linkplain ClassPlugins#entries resolved} plugin registration. Due to the 373 * possibility of class redefinition, we cannot directly use {@link ResolvedJavaMethod}s as 374 * keys. A {@link ResolvedJavaMethod} implementation might implement {@code equals()} and 375 * {@code hashCode()} based on internal representation subject to change by class redefinition. 376 */ 377 static final class ResolvedJavaMethodKey { 378 private final ResolvedJavaMethod method; 379 380 ResolvedJavaMethodKey(ResolvedJavaMethod method) { 381 this.method = method; 382 } 383 384 @Override 385 public boolean equals(Object obj) { 386 if (obj instanceof ResolvedJavaMethodKey) { 387 ResolvedJavaMethodKey that = (ResolvedJavaMethodKey) obj; 388 if (this.method.isStatic() == that.method.isStatic()) { 389 if (this.method.getDeclaringClass().equals(that.method.getDeclaringClass())) { 390 if (this.method.getName().equals(that.method.getName())) { 391 if (this.method.getSignature().equals(that.method.getSignature())) { 392 return true; 393 } 394 } 395 } 396 } 397 } 398 return false; 399 } 400 401 @Override 402 public int hashCode() { 403 return this.method.getName().hashCode(); 404 } 405 406 @Override 407 public String toString() { 408 return "ResolvedJavaMethodKey<" + method + ">"; 409 } 410 } 411 412 /** 413 * Key for {@linkplain ClassPlugins#registrations registering} an {@link InvocationPlugin} for a 414 * specific method. 415 */ 416 static class MethodKey { 417 final boolean isStatic; 418 419 /** 420 * This method is optional. This is used for new API methods not present in previous JDK 421 * versions. 422 */ 423 final boolean isOptional; 424 425 final String name; 426 final Type[] argumentTypes; 427 final InvocationPlugin value; 428 429 /** 430 * Used to lazily initialize {@link #resolved}. 431 */ 432 private final MetaAccessProvider metaAccess; 433 434 private volatile ResolvedJavaMethod resolved; 435 436 MethodKey(MetaAccessProvider metaAccess, InvocationPlugin data, boolean isStatic, boolean isOptional, String name, Type... argumentTypes) { 437 this.metaAccess = metaAccess; 438 this.value = data; 439 this.isStatic = isStatic; 440 this.isOptional = isOptional; 441 this.name = name; 442 this.argumentTypes = argumentTypes; 443 } 444 445 @Override 446 public boolean equals(Object obj) { 447 if (obj instanceof MethodKey) { 448 MethodKey that = (MethodKey) obj; 449 boolean res = this.name.equals(that.name) && areEqual(this.argumentTypes, that.argumentTypes); 450 assert !res || this.isStatic == that.isStatic; 451 return res; 452 } 453 return false; 454 } 455 456 private static boolean areEqual(Type[] args1, Type[] args2) { 457 if (args1.length == args2.length) { 458 for (int i = 0; i < args1.length; i++) { 459 if (!args1[i].getTypeName().equals(args2[i].getTypeName())) { 460 return false; 461 } 462 } 463 return true; 464 } 465 return false; 466 } 467 468 public int getDeclaredParameterCount() { 469 return isStatic ? argumentTypes.length : argumentTypes.length - 1; 470 } 471 472 @Override 473 public int hashCode() { 474 return name.hashCode(); 475 } 476 477 private ResolvedJavaMethod resolve(Class<?> declaringClass) { 478 if (resolved == null) { 479 Executable method = resolveJava(declaringClass); 480 if (method == null) { 481 return null; 482 } 483 resolved = metaAccess.lookupJavaMethod(method); 484 } 485 return resolved; 486 } 487 488 private Executable resolveJava(Class<?> declaringClass) { 489 try { 490 Executable res; 491 Class<?>[] parameterTypes = resolveTypes(argumentTypes, isStatic ? 0 : 1, argumentTypes.length); 492 if (name.equals("<init>")) { 493 res = declaringClass.getDeclaredConstructor(parameterTypes); 494 } else { 495 res = declaringClass.getDeclaredMethod(name, parameterTypes); 496 } 497 assert Modifier.isStatic(res.getModifiers()) == isStatic : res; 498 return res; 499 } catch (NoSuchMethodException | SecurityException e) { 500 if (isOptional) { 501 return null; 502 } 503 throw new InternalError(e); 504 } 505 } 506 507 @Override 508 public String toString() { 509 StringBuilder sb = new StringBuilder(name).append('('); 510 for (Type p : argumentTypes) { 511 if (sb.charAt(sb.length() - 1) != '(') { 512 sb.append(", "); 513 } 514 sb.append(p.getTypeName()); 515 } 516 return sb.append(')').toString(); 517 } 518 } 519 520 private final MetaAccessProvider metaAccess; 521 522 private final EconomicMap<String, ClassPlugins> registrations = EconomicMap.create(Equivalence.DEFAULT); 523 524 /** 525 * Deferred registrations as well as guard for initialization. The guard uses double-checked 526 * locking which is why this field is {@code volatile}. 527 */ 528 private volatile List<Runnable> deferredRegistrations = new ArrayList<>(); 529 530 /** 531 * Adds a {@link Runnable} for doing registration deferred until the first time 532 * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object. 533 */ 534 public void defer(Runnable deferrable) { 535 assert deferredRegistrations != null : "registration is closed"; 536 deferredRegistrations.add(deferrable); 537 } 538 539 /** 540 * Per-class invocation plugins. 541 */ 542 protected static class ClassPlugins { 543 private final Type declaringType; 544 545 private final List<MethodKey> registrations = new ArrayList<>(); 546 547 public ClassPlugins(Type declaringClass) { 548 this.declaringType = declaringClass; 549 } 550 551 /** 552 * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}. 553 * 554 * Note: this must be volatile as threads may race to initialize it. 555 */ 556 private volatile EconomicMap<ResolvedJavaMethodKey, InvocationPlugin> entries; 557 558 void initializeMap() { 559 if (!isClosed()) { 560 if (registrations.isEmpty()) { 561 entries = EconomicMap.create(Equivalence.DEFAULT); 562 } else { 563 Class<?> declaringClass = resolveType(declaringType, true); 564 if (declaringClass == null) { 565 // An optional type that could not be resolved 566 entries = EconomicMap.create(Equivalence.DEFAULT); 567 } else { 568 EconomicMap<ResolvedJavaMethodKey, InvocationPlugin> newEntries = EconomicMap.create(Equivalence.DEFAULT); 569 for (MethodKey methodKey : registrations) { 570 ResolvedJavaMethod m = methodKey.resolve(declaringClass); 571 if (m != null) { 572 newEntries.put(new ResolvedJavaMethodKey(m), methodKey.value); 573 if (entries != null) { 574 // Another thread finished initializing entries first 575 return; 576 } 577 } 578 } 579 entries = newEntries; 580 } 581 } 582 } 583 } 584 585 public InvocationPlugin get(ResolvedJavaMethod method) { 586 if (!isClosed()) { 587 initializeMap(); 588 } 589 return entries.get(new ResolvedJavaMethodKey(method)); 590 } 591 592 public void register(MethodKey methodKey, boolean allowOverwrite) { 593 assert !isClosed() : "registration is closed: " + methodKey; 594 if (allowOverwrite) { 595 int index = registrations.indexOf(methodKey); 596 if (index >= 0) { 597 registrations.set(index, methodKey); 598 return; 599 } 600 } else { 601 assert !registrations.contains(methodKey) : "a value is already registered for " + declaringType + "." + methodKey; 602 } 603 registrations.add(methodKey); 604 } 605 606 public boolean isClosed() { 607 return entries != null; 608 } 609 } 610 611 /** 612 * Adds an entry to this map for a specified method. 613 * 614 * @param value value to be associated with the specified method 615 * @param isStatic specifies if the method is static 616 * @param isOptional specifies if the method is optional 617 * @param declaringClass the class declaring the method 618 * @param name the name of the method 619 * @param argumentTypes the argument types of the method. Element 0 of this array must be 620 * {@code declaringClass} iff the method is non-static. 621 * @return an object representing the method 622 */ 623 MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { 624 assert isStatic || argumentTypes[0] == declaringClass; 625 626 String internalName = MetaUtil.toInternalName(declaringClass.getTypeName()); 627 ClassPlugins classPlugins = registrations.get(internalName); 628 if (classPlugins == null) { 629 classPlugins = new ClassPlugins(declaringClass); 630 registrations.put(internalName, classPlugins); 631 } 632 assert isStatic || argumentTypes[0] == declaringClass; 633 MethodKey methodKey = new MethodKey(metaAccess, value, isStatic, isOptional, name, argumentTypes); 634 classPlugins.register(methodKey, allowOverwrite); 635 return methodKey; 636 } 637 638 /** 639 * Determines if a method denoted by a given {@link MethodKey} is in this map. 640 */ 641 boolean containsKey(Type declaringType, MethodKey key) { 642 String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); 643 ClassPlugins classPlugins = registrations.get(internalName); 644 return classPlugins != null && classPlugins.registrations.contains(key); 645 } 646 647 InvocationPlugin get(ResolvedJavaMethod method) { 648 flushDeferrables(); 649 String internalName = method.getDeclaringClass().getName(); 650 ClassPlugins classPlugins = registrations.get(internalName); 651 if (classPlugins != null) { 652 return classPlugins.get(method); 653 } 654 return null; 655 } 656 657 private void flushDeferrables() { 658 if (deferredRegistrations != null) { 659 synchronized (this) { 660 if (deferredRegistrations != null) { 661 for (Runnable deferrable : deferredRegistrations) { 662 deferrable.run(); 663 } 664 deferredRegistrations = null; 665 } 666 } 667 for (ClassPlugins e : registrations.getValues()) { 668 e.initializeMap(); 669 } 670 } 671 } 672 673 /** 674 * Disallows new registrations of new plugins, and creates the internal tables for method 675 * lookup. 676 */ 677 public void closeRegistration() { 678 flushDeferrables(); 679 for (ClassPlugins e : registrations.getValues()) { 680 e.initializeMap(); 681 } 682 } 683 684 public int size() { 685 return registrations.size(); 686 } 687 688 /** 689 * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in 690 * this object. 691 */ 692 protected final InvocationPlugins parent; 693 694 private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { 695 this.metaAccess = metaAccess; 696 InvocationPlugins p = parent; 697 this.parent = p; 698 } 699 700 /** 701 * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. 702 */ 703 public InvocationPlugins(InvocationPlugins parent) { 704 this(parent, parent.getMetaAccess()); 705 } 706 707 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent, MetaAccessProvider metaAccess) { 708 this.metaAccess = metaAccess; 709 this.parent = parent; 710 711 this.deferredRegistrations = null; 712 713 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) { 714 ResolvedJavaMethod method = entry.getKey(); 715 InvocationPlugin plugin = entry.getValue(); 716 717 String internalName = method.getDeclaringClass().getName(); 718 ClassPlugins classPlugins = registrations.get(internalName); 719 if (classPlugins == null) { 720 classPlugins = new ClassPlugins(null); 721 registrations.put(internalName, classPlugins); 722 classPlugins.entries = EconomicMap.create(Equivalence.DEFAULT); 723 } 724 725 classPlugins.entries.put(new ResolvedJavaMethodKey(method), plugin); 726 } 727 } 728 729 public MetaAccessProvider getMetaAccess() { 730 return metaAccess; 731 } 732 733 public InvocationPlugins(MetaAccessProvider metaAccess) { 734 this(null, metaAccess); 735 } 736 737 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { 738 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; 739 if (!isStatic) { 740 argumentTypes[0] = declaringClass; 741 } 742 MethodKey methodKey = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes); 743 assert Checker.check(this, declaringClass, methodKey, plugin); 744 } 745 746 /** 747 * Registers an invocation plugin for a given method. There must be no plugin currently 748 * registered for {@code method}. 749 * 750 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 751 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 752 * non-static. Upon returning, element 0 will have been rewritten to 753 * {@code declaringClass} 754 */ 755 public void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) { 756 register(plugin, false, false, declaringClass, name, argumentTypes); 757 } 758 759 public void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) { 760 register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes); 761 } 762 763 /** 764 * Registers an invocation plugin for a given, optional method. There must be no plugin 765 * currently registered for {@code method}. 766 * 767 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 768 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 769 * non-static. Upon returning, element 0 will have been rewritten to 770 * {@code declaringClass} 771 */ 772 public void registerOptional(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) { 773 register(plugin, true, false, declaringClass, name, argumentTypes); 774 } 775 776 /** 777 * Gets the plugin for a given method. 778 * 779 * @param method the method to lookup 780 * @return the plugin associated with {@code method} or {@code null} if none exists 781 */ 782 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { 783 if (parent != null) { 784 InvocationPlugin plugin = parent.lookupInvocation(method); 785 if (plugin != null) { 786 return plugin; 787 } 788 } 789 return get(method); 790 } 791 792 /** 793 * Gets the set of methods for which invocation plugins have been registered. Once this method 794 * is called, no further registrations can be made. 795 */ 796 public EconomicSet<ResolvedJavaMethod> getMethods() { 797 EconomicSet<ResolvedJavaMethod> res = EconomicSet.create(Equivalence.DEFAULT); 798 if (parent != null) { 799 res.addAll(parent.getMethods()); 800 } 801 flushDeferrables(); 802 for (ClassPlugins cp : registrations.getValues()) { 803 for (ResolvedJavaMethodKey key : cp.entries.getKeys()) { 804 res.add(key.method); 805 } 806 } 807 return res; 808 } 809 810 /** 811 * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} 812 * before searching in this object. 813 */ 814 public InvocationPlugins getParent() { 815 return parent; 816 } 817 818 @Override 819 public String toString() { 820 StringBuilder buf = new StringBuilder(); 821 UnmodifiableMapCursor<String, ClassPlugins> entries = registrations.getEntries(); 822 while (entries.advance()) { 823 buf.append(entries.getKey()).append('.').append(entries.getValue()).append(", "); 824 } 825 826 String s = buf.toString(); 827 if (buf.length() != 0) { 828 s = s.substring(buf.length() - ", ".length()); 829 } 830 return s + " / parent: " + this.parent; 831 } 832 833 private static class Checker { 834 private static final int MAX_ARITY = 7; 835 /** 836 * The set of all {@link InvocationPlugin#apply} method signatures. 837 */ 838 static final Class<?>[][] SIGS; 839 840 static { 841 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY); 842 for (Method method : InvocationPlugin.class.getDeclaredMethods()) { 843 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) { 844 Class<?>[] sig = method.getParameterTypes(); 845 assert sig[0] == GraphBuilderContext.class; 846 assert sig[1] == ResolvedJavaMethod.class; 847 assert sig[2] == InvocationPlugin.Receiver.class; 848 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class); 849 while (sigs.size() < sig.length - 2) { 850 sigs.add(null); 851 } 852 sigs.set(sig.length - 3, sig); 853 } 854 } 855 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), 856 ValueNode.class.getSimpleName()); 857 SIGS = sigs.toArray(new Class<?>[sigs.size()][]); 858 } 859 860 public static boolean check(InvocationPlugins plugins, Type declaringType, MethodKey method, InvocationPlugin plugin) { 861 InvocationPlugins p = plugins.parent; 862 while (p != null) { 863 assert !p.containsKey(declaringType, method) : "a plugin is already registered for " + method; 864 p = p.parent; 865 } 866 if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) { 867 return true; 868 } 869 if (plugin instanceof MethodSubstitutionPlugin) { 870 MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; 871 Method substitute = msplugin.getJavaSubstitute(); 872 assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute); 873 return true; 874 } 875 int arguments = method.getDeclaredParameterCount(); 876 assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method); 877 for (Method m : plugin.getClass().getDeclaredMethods()) { 878 if (m.getName().equals("apply")) { 879 Class<?>[] parameterTypes = m.getParameterTypes(); 880 if (Arrays.equals(SIGS[arguments], parameterTypes)) { 881 return true; 882 } 883 } 884 } 885 throw new AssertionError(format("graph builder plugin for %s not found", method)); 886 } 887 } 888 889 /** 890 * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. 891 * 892 * @param b the graph builder that applied the plugin 893 * @param plugin a plugin that was just applied 894 * @param newNodes the nodes added to the graph by {@code plugin} 895 * @throws AssertionError if any check fail 896 */ 897 public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) { 898 if (parent != null) { 899 parent.checkNewNodes(b, plugin, newNodes); 900 } 901 } 902 903 /** 904 * Resolves a name to a class. 905 * 906 * @param className the name of the class to resolve 907 * @param optional if true, resolution failure returns null 908 * @return the resolved class or null if resolution fails and {@code optional} is true 909 */ 910 public static Class<?> resolveClass(String className, boolean optional) { 911 try { 912 // Need to use the system class loader to handle classes 913 // loaded by the application class loader which is not 914 // delegated to by the JVMCI class loader. 915 ClassLoader cl = ClassLoader.getSystemClassLoader(); 916 return Class.forName(className, false, cl); 917 } catch (ClassNotFoundException e) { 918 if (optional) { 919 return null; 920 } 921 throw new GraalError("Could not resolve type " + className); 922 } 923 } 924 925 /** 926 * Resolves a {@link Type} to a {@link Class}. 927 * 928 * @param type the type to resolve 929 * @param optional if true, resolution failure returns null 930 * @return the resolved class or null if resolution fails and {@code optional} is true 931 */ 932 public static Class<?> resolveType(Type type, boolean optional) { 933 if (type instanceof Class) { 934 return (Class<?>) type; 935 } 936 if (optional && type instanceof OptionalLazySymbol) { 937 return ((OptionalLazySymbol) type).resolve(); 938 } 939 return resolveClass(type.getTypeName(), optional); 940 } 941 942 private static final Class<?>[] NO_CLASSES = {}; 943 944 /** 945 * Resolves an array of {@link Type}s to an array of {@link Class}es. 946 * 947 * @param types the types to resolve 948 * @param from the initial index of the range to be resolved, inclusive 949 * @param to the final index of the range to be resolved, exclusive 950 * @return the resolved class or null if resolution fails and {@code optional} is true 951 */ 952 public static Class<?>[] resolveTypes(Type[] types, int from, int to) { 953 int length = to - from; 954 if (length <= 0) { 955 return NO_CLASSES; 956 } 957 Class<?>[] classes = new Class<?>[length]; 958 for (int i = 0; i < length; i++) { 959 classes[i] = resolveType(types[i + from], false); 960 } 961 return classes; 962 } 963 }