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.Collections; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 40 import org.graalvm.compiler.api.replacements.MethodSubstitution; 41 import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry; 42 import org.graalvm.compiler.bytecode.BytecodeProvider; 43 import org.graalvm.compiler.debug.GraalError; 44 import org.graalvm.compiler.graph.Node; 45 import org.graalvm.compiler.graph.iterators.NodeIterable; 46 import org.graalvm.compiler.nodes.ValueNode; 47 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver; 48 49 import jdk.vm.ci.meta.MetaAccessProvider; 50 import jdk.vm.ci.meta.MetaUtil; 51 import jdk.vm.ci.meta.ResolvedJavaMethod; 52 53 /** 54 * Manages a set of {@link InvocationPlugin}s. 55 */ 56 public class InvocationPlugins { 57 58 public static class InvocationPluginReceiver implements InvocationPlugin.Receiver { 59 private final GraphBuilderContext parser; 60 private ValueNode[] args; 61 private ValueNode value; 62 63 public InvocationPluginReceiver(GraphBuilderContext parser) { 64 this.parser = parser; 65 } 66 67 @Override 68 public ValueNode get(boolean performNullCheck) { 69 assert args != null : "Cannot get the receiver of a static method"; 70 if (!performNullCheck) { 71 return args[0]; 72 } 73 if (value == null) { 74 value = parser.nullCheckedValue(args[0]); 243 * Registers a plugin for a method with 4 arguments. 244 * 245 * @param name the name of the method 246 * @param plugin the plugin to be registered 247 */ 248 public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) { 249 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4); 250 } 251 252 /** 253 * Registers a plugin for a method with 5 arguments. 254 * 255 * @param name the name of the method 256 * @param plugin the plugin to be registered 257 */ 258 public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) { 259 plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5); 260 } 261 262 /** 263 * Registers a plugin for an optional method with no arguments. 264 * 265 * @param name the name of the method 266 * @param plugin the plugin to be registered 267 */ 268 public void registerOptional0(String name, InvocationPlugin plugin) { 269 plugins.register(plugin, true, allowOverwrite, declaringType, name); 270 } 271 272 /** 273 * Registers a plugin for an optional method with 1 argument. 274 * 275 * @param name the name of the method 276 * @param plugin the plugin to be registered 277 */ 278 public void registerOptional1(String name, Type arg, InvocationPlugin plugin) { 279 plugins.register(plugin, true, allowOverwrite, declaringType, name, arg); 280 } 281 282 /** 320 * {@code declaringClass} 321 */ 322 @Override 323 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) { 324 registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes); 325 } 326 327 /** 328 * Registers a plugin that implements a method based on the bytecode of a substitute method. 329 * 330 * @param substituteDeclaringClass the class declaring the substitute method 331 * @param name the name of both the original method 332 * @param substituteName the name of the substitute method 333 * @param argumentTypes the argument types of the method. Element 0 of this array must be 334 * the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method 335 * is non-static. Upon returning, element 0 will have been rewritten to 336 * {@code declaringClass} 337 */ 338 @Override 339 public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) { 340 assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider"; 341 MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes); 342 plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes); 343 } 344 } 345 346 /** 347 * Key for a {@linkplain ClassPlugins#entries resolved} plugin registration. Due to the 348 * possibility of class redefinition, we cannot directly use {@link ResolvedJavaMethod}s as 349 * keys. A {@link ResolvedJavaMethod} implementation might implement {@code equals()} and 350 * {@code hashCode()} based on internal representation subject to change by class redefinition. 351 */ 352 static final class ResolvedJavaMethodKey { 353 private final ResolvedJavaMethod method; 354 355 ResolvedJavaMethodKey(ResolvedJavaMethod method) { 356 this.method = method; 357 } 358 359 @Override 360 public boolean equals(Object obj) { 361 if (obj instanceof ResolvedJavaMethodKey) { 362 ResolvedJavaMethodKey that = (ResolvedJavaMethodKey) obj; 363 if (this.method.isStatic() == that.method.isStatic()) { 364 if (this.method.getDeclaringClass().equals(that.method.getDeclaringClass())) { 365 if (this.method.getName().equals(that.method.getName())) { 366 if (this.method.getSignature().equals(that.method.getSignature())) { 367 return true; 368 } 369 } 370 } 371 } 372 } 373 return false; 374 } 375 376 @Override 377 public int hashCode() { 378 return this.method.getName().hashCode(); 379 } 380 381 @Override 382 public String toString() { 383 return "ResolvedJavaMethodKey<" + method + ">"; 384 } 385 } 386 387 /** 388 * Key for {@linkplain ClassPlugins#registrations registering} an {@link InvocationPlugin} for a 389 * specific method. 390 */ 391 static class MethodKey { 392 final boolean isStatic; 393 394 /** 395 * This method is optional. This is used for new API methods not present in previous JDK 396 * versions. 397 */ 398 final boolean isOptional; 399 400 final String name; 401 final Type[] argumentTypes; 402 final InvocationPlugin value; 403 404 /** 405 * Used to lazily initialize {@link #resolved}. 406 */ 407 private final MetaAccessProvider metaAccess; 408 409 private volatile ResolvedJavaMethod resolved; 410 411 MethodKey(MetaAccessProvider metaAccess, InvocationPlugin data, boolean isStatic, boolean isOptional, String name, Type... argumentTypes) { 412 this.metaAccess = metaAccess; 413 this.value = data; 414 this.isStatic = isStatic; 415 this.isOptional = isOptional; 416 this.name = name; 417 this.argumentTypes = argumentTypes; 418 } 419 420 @Override 421 public boolean equals(Object obj) { 422 if (obj instanceof MethodKey) { 423 MethodKey that = (MethodKey) obj; 424 boolean res = this.name.equals(that.name) && areEqual(this.argumentTypes, that.argumentTypes); 425 assert !res || this.isStatic == that.isStatic; 426 return res; 427 } 428 return false; 429 } 430 431 private static boolean areEqual(Type[] args1, Type[] args2) { 432 if (args1.length == args2.length) { 433 for (int i = 0; i < args1.length; i++) { 434 if (!args1[i].getTypeName().equals(args2[i].getTypeName())) { 435 return false; 436 } 437 } 438 return true; 439 } 440 return false; 441 } 442 443 public int getDeclaredParameterCount() { 444 return isStatic ? argumentTypes.length : argumentTypes.length - 1; 445 } 446 447 @Override 448 public int hashCode() { 449 return name.hashCode(); 450 } 451 452 private ResolvedJavaMethod resolve(Class<?> declaringClass) { 453 if (resolved == null) { 454 Executable method = resolveJava(declaringClass); 455 if (method == null) { 456 return null; 457 } 458 resolved = metaAccess.lookupJavaMethod(method); 459 } 460 return resolved; 461 } 462 463 private Executable resolveJava(Class<?> declaringClass) { 464 try { 465 Executable res; 466 Class<?>[] parameterTypes = resolveTypes(argumentTypes, isStatic ? 0 : 1, argumentTypes.length); 467 if (name.equals("<init>")) { 468 res = declaringClass.getDeclaredConstructor(parameterTypes); 469 } else { 470 res = declaringClass.getDeclaredMethod(name, parameterTypes); 471 } 472 assert Modifier.isStatic(res.getModifiers()) == isStatic : res; 473 return res; 474 } catch (NoSuchMethodException | SecurityException e) { 475 if (isOptional) { 476 return null; 477 } 478 throw new InternalError(e); 479 } 480 } 481 482 @Override 483 public String toString() { 484 StringBuilder sb = new StringBuilder(name).append('('); 485 for (Type p : argumentTypes) { 486 if (sb.charAt(sb.length() - 1) != '(') { 487 sb.append(", "); 488 } 489 sb.append(p.getTypeName()); 490 } 491 return sb.append(')').toString(); 492 } 493 } 494 495 private final MetaAccessProvider metaAccess; 496 497 private final Map<String, ClassPlugins> registrations = new HashMap<>(); 498 499 /** 500 * Deferred registrations as well as guard for initialization. The guard uses double-checked 501 * locking which is why this field is {@code volatile}. 502 */ 503 private volatile List<Runnable> deferredRegistrations = new ArrayList<>(); 504 505 /** 506 * Adds a {@link Runnable} for doing registration deferred until the first time 507 * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object. 508 */ 509 public void defer(Runnable deferrable) { 510 assert deferredRegistrations != null : "registration is closed"; 511 deferredRegistrations.add(deferrable); 512 } 513 514 /** 515 * Per-class invocation plugins. 516 */ 517 protected static class ClassPlugins { 518 private final Type declaringType; 519 520 private final List<MethodKey> registrations = new ArrayList<>(); 521 522 public ClassPlugins(Type declaringClass) { 523 this.declaringType = declaringClass; 524 } 525 526 /** 527 * Entry map that is initialized upon first call to {@link #get(ResolvedJavaMethod)}. 528 * 529 * Note: this must be volatile as threads may race to initialize it. 530 */ 531 private volatile Map<ResolvedJavaMethodKey, InvocationPlugin> entries; 532 533 void initializeMap() { 534 if (!isClosed()) { 535 if (registrations.isEmpty()) { 536 entries = Collections.emptyMap(); 537 } else { 538 Class<?> declaringClass = resolveType(declaringType, true); 539 if (declaringClass == null) { 540 // An optional type that could not be resolved 541 entries = Collections.emptyMap(); 542 } else { 543 Map<ResolvedJavaMethodKey, InvocationPlugin> newEntries = new HashMap<>(); 544 for (MethodKey methodKey : registrations) { 545 ResolvedJavaMethod m = methodKey.resolve(declaringClass); 546 if (m != null) { 547 newEntries.put(new ResolvedJavaMethodKey(m), methodKey.value); 548 if (entries != null) { 549 // Another thread finished initializing entries first 550 return; 551 } 552 } 553 } 554 entries = newEntries; 555 } 556 } 557 } 558 } 559 560 public InvocationPlugin get(ResolvedJavaMethod method) { 561 if (!isClosed()) { 562 initializeMap(); 563 } 564 return entries.get(new ResolvedJavaMethodKey(method)); 565 } 566 567 public void register(MethodKey methodKey, boolean allowOverwrite) { 568 assert !isClosed() : "registration is closed: " + methodKey + " " + Arrays.toString(entries.keySet().toArray()); 569 if (allowOverwrite) { 570 int index = registrations.indexOf(methodKey); 571 if (index >= 0) { 572 registrations.set(index, methodKey); 573 return; 574 } 575 } else { 576 assert !registrations.contains(methodKey) : "a value is already registered for " + declaringType + "." + methodKey; 577 } 578 registrations.add(methodKey); 579 } 580 581 public boolean isClosed() { 582 return entries != null; 583 } 584 } 585 586 /** 587 * Adds an entry to this map for a specified method. 588 * 589 * @param value value to be associated with the specified method 590 * @param isStatic specifies if the method is static 591 * @param isOptional specifies if the method is optional 592 * @param declaringClass the class declaring the method 593 * @param name the name of the method 594 * @param argumentTypes the argument types of the method. Element 0 of this array must be 595 * {@code declaringClass} iff the method is non-static. 596 * @return an object representing the method 597 */ 598 MethodKey put(InvocationPlugin value, boolean isStatic, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { 599 assert isStatic || argumentTypes[0] == declaringClass; 600 601 String internalName = MetaUtil.toInternalName(declaringClass.getTypeName()); 602 ClassPlugins classPlugins = registrations.get(internalName); 603 if (classPlugins == null) { 604 classPlugins = new ClassPlugins(declaringClass); 605 registrations.put(internalName, classPlugins); 606 } 607 assert isStatic || argumentTypes[0] == declaringClass; 608 MethodKey methodKey = new MethodKey(metaAccess, value, isStatic, isOptional, name, argumentTypes); 609 classPlugins.register(methodKey, allowOverwrite); 610 return methodKey; 611 } 612 613 /** 614 * Determines if a method denoted by a given {@link MethodKey} is in this map. 615 */ 616 boolean containsKey(Type declaringType, MethodKey key) { 617 String internalName = MetaUtil.toInternalName(declaringType.getTypeName()); 618 ClassPlugins classPlugins = registrations.get(internalName); 619 return classPlugins != null && classPlugins.registrations.contains(key); 620 } 621 622 InvocationPlugin get(ResolvedJavaMethod method) { 623 flushDeferrables(); 624 String internalName = method.getDeclaringClass().getName(); 625 ClassPlugins classPlugins = registrations.get(internalName); 626 if (classPlugins != null) { 627 return classPlugins.get(method); 628 } 629 return null; 630 } 631 632 private void flushDeferrables() { 633 if (deferredRegistrations != null) { 634 synchronized (this) { 635 if (deferredRegistrations != null) { 636 for (Runnable deferrable : deferredRegistrations) { 637 deferrable.run(); 638 } 639 deferredRegistrations = null; 640 } 641 } 642 for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) { 643 e.getValue().initializeMap(); 644 } 645 } 646 } 647 648 /** 649 * Disallows new registrations of new plugins, and creates the internal tables for method 650 * lookup. 651 */ 652 public void closeRegistration() { 653 flushDeferrables(); 654 for (Map.Entry<String, ClassPlugins> e : registrations.entrySet()) { 655 e.getValue().initializeMap(); 656 } 657 } 658 659 public int size() { 660 return registrations.size(); 661 } 662 663 /** 664 * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in 665 * this object. 666 */ 667 protected final InvocationPlugins parent; 668 669 private InvocationPlugins(InvocationPlugins parent, MetaAccessProvider metaAccess) { 670 this.metaAccess = metaAccess; 671 InvocationPlugins p = parent; 672 this.parent = p; 673 } 674 675 /** 676 * Creates a set of invocation plugins with a non-null {@linkplain #getParent() parent}. 677 */ 678 public InvocationPlugins(InvocationPlugins parent) { 679 this(parent, parent.getMetaAccess()); 680 } 681 682 public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent, MetaAccessProvider metaAccess) { 683 this.metaAccess = metaAccess; 684 this.parent = parent; 685 686 this.deferredRegistrations = null; 687 688 for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) { 689 ResolvedJavaMethod method = entry.getKey(); 690 InvocationPlugin plugin = entry.getValue(); 691 692 String internalName = method.getDeclaringClass().getName(); 693 ClassPlugins classPlugins = registrations.get(internalName); 694 if (classPlugins == null) { 695 classPlugins = new ClassPlugins(null); 696 registrations.put(internalName, classPlugins); 697 classPlugins.entries = new HashMap<>(); 698 } 699 700 classPlugins.entries.put(new ResolvedJavaMethodKey(method), plugin); 701 } 702 } 703 704 public MetaAccessProvider getMetaAccess() { 705 return metaAccess; 706 } 707 708 public InvocationPlugins(MetaAccessProvider metaAccess) { 709 this(null, metaAccess); 710 } 711 712 protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) { 713 boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class; 714 if (!isStatic) { 715 argumentTypes[0] = declaringClass; 716 } 717 MethodKey methodKey = put(plugin, isStatic, isOptional, allowOverwrite, declaringClass, name, argumentTypes); 718 assert Checker.check(this, declaringClass, methodKey, plugin); 719 } 720 721 /** 722 * Registers an invocation plugin for a given method. There must be no plugin currently 723 * registered for {@code method}. 724 * 725 * @param argumentTypes the argument types of the method. Element 0 of this array must be the 726 * {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is 727 * non-static. Upon returning, element 0 will have been rewritten to 728 * {@code declaringClass} 729 */ 730 public void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) { 731 register(plugin, false, false, declaringClass, name, argumentTypes); 732 } 733 734 public void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) { 735 register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes); 736 } 737 738 /** 748 register(plugin, true, false, declaringClass, name, argumentTypes); 749 } 750 751 /** 752 * Gets the plugin for a given method. 753 * 754 * @param method the method to lookup 755 * @return the plugin associated with {@code method} or {@code null} if none exists 756 */ 757 public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) { 758 if (parent != null) { 759 InvocationPlugin plugin = parent.lookupInvocation(method); 760 if (plugin != null) { 761 return plugin; 762 } 763 } 764 return get(method); 765 } 766 767 /** 768 * Gets the set of methods for which invocation plugins have been registered. Once this method 769 * is called, no further registrations can be made. 770 */ 771 public Set<ResolvedJavaMethod> getMethods() { 772 Set<ResolvedJavaMethod> res = new HashSet<>(); 773 if (parent != null) { 774 res.addAll(parent.getMethods()); 775 } 776 flushDeferrables(); 777 for (ClassPlugins cp : registrations.values()) { 778 for (ResolvedJavaMethodKey key : cp.entries.keySet()) { 779 res.add(key.method); 780 } 781 } 782 return res; 783 } 784 785 /** 786 * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} 787 * before searching in this object. 788 */ 789 public InvocationPlugins getParent() { 790 return parent; 791 } 792 793 @Override 794 public String toString() { 795 StringBuilder buf = new StringBuilder(); 796 registrations.forEach((name, cp) -> buf.append(name).append('.').append(cp).append(", ")); 797 String s = buf.toString(); 798 if (buf.length() != 0) { 799 s = s.substring(buf.length() - ", ".length()); 800 } 801 return s + " / parent: " + this.parent; 802 } 803 804 private static class Checker { 805 private static final int MAX_ARITY = 5; 806 /** 807 * The set of all {@link InvocationPlugin#apply} method signatures. 808 */ 809 static final Class<?>[][] SIGS; 810 811 static { 812 ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY); 813 for (Method method : InvocationPlugin.class.getDeclaredMethods()) { 814 if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) { 815 Class<?>[] sig = method.getParameterTypes(); 816 assert sig[0] == GraphBuilderContext.class; 817 assert sig[1] == ResolvedJavaMethod.class; 818 assert sig[2] == InvocationPlugin.Receiver.class; 819 assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class); 820 while (sigs.size() < sig.length - 2) { 821 sigs.add(null); 822 } 823 sigs.set(sig.length - 3, sig); 824 } 825 } 826 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), 827 ValueNode.class.getSimpleName()); 828 SIGS = sigs.toArray(new Class<?>[sigs.size()][]); 829 } 830 831 public static boolean check(InvocationPlugins plugins, Type declaringType, MethodKey method, InvocationPlugin plugin) { 832 InvocationPlugins p = plugins.parent; 833 while (p != null) { 834 assert !p.containsKey(declaringType, method) : "a plugin is already registered for " + method; 835 p = p.parent; 836 } 837 if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) { 838 return true; 839 } 840 if (plugin instanceof MethodSubstitutionPlugin) { 841 MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin; 842 Method substitute = msplugin.getJavaSubstitute(); 843 assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute); 844 return true; 845 } 846 int arguments = method.getDeclaredParameterCount(); 847 assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, method); 848 for (Method m : plugin.getClass().getDeclaredMethods()) { 849 if (m.getName().equals("apply")) { 850 Class<?>[] parameterTypes = m.getParameterTypes(); 851 if (Arrays.equals(SIGS[arguments], parameterTypes)) { 852 return true; 853 } 854 } 855 } 856 throw new AssertionError(format("graph builder plugin for %s not found", method)); 857 } 858 } 859 860 /** 861 * Checks a set of nodes added to the graph by an {@link InvocationPlugin}. 862 * 863 * @param b the graph builder that applied the plugin 864 * @param plugin a plugin that was just applied 865 * @param newNodes the nodes added to the graph by {@code plugin} 866 * @throws AssertionError if any check fail 867 */ 868 public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) { 869 if (parent != null) { 870 parent.checkNewNodes(b, plugin, newNodes); 871 } 872 } 873 874 /** 875 * Resolves a name to a class. 876 * 887 return Class.forName(className, false, cl); 888 } catch (ClassNotFoundException e) { 889 if (optional) { 890 return null; 891 } 892 throw new GraalError("Could not resolve type " + className); 893 } 894 } 895 896 /** 897 * Resolves a {@link Type} to a {@link Class}. 898 * 899 * @param type the type to resolve 900 * @param optional if true, resolution failure returns null 901 * @return the resolved class or null if resolution fails and {@code optional} is true 902 */ 903 public static Class<?> resolveType(Type type, boolean optional) { 904 if (type instanceof Class) { 905 return (Class<?>) type; 906 } 907 if (optional && type instanceof OptionalLazySymbol) { 908 return ((OptionalLazySymbol) type).resolve(); 909 } 910 return resolveClass(type.getTypeName(), optional); 911 } 912 913 private static final Class<?>[] NO_CLASSES = {}; 914 915 /** 916 * Resolves an array of {@link Type}s to an array of {@link Class}es. 917 * 918 * @param types the types to resolve 919 * @param from the initial index of the range to be resolved, inclusive 920 * @param to the final index of the range to be resolved, exclusive 921 * @return the resolved class or null if resolution fails and {@code optional} is true 922 */ 923 public static Class<?>[] resolveTypes(Type[] types, int from, int to) { 924 int length = to - from; 925 if (length <= 0) { 926 return NO_CLASSES; 927 } 928 Class<?>[] classes = new Class<?>[length]; 929 for (int i = 0; i < length; i++) { 930 classes[i] = resolveType(types[i + from], false); 931 } 932 return classes; 933 } 934 } |