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 */
23package org.graalvm.compiler.nodes.graphbuilderconf;
24
25import static java.lang.String.format;
26import static org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.LateClassPlugins.CLOSED_LATE_CLASS_PLUGIN;
27
28import java.lang.reflect.Constructor;
29import java.lang.reflect.Method;
30import java.lang.reflect.Modifier;
31import java.lang.reflect.Type;
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.Collections;
35import java.util.List;
36import java.util.Map;
37
38import org.graalvm.compiler.api.replacements.MethodSubstitution;
39import org.graalvm.compiler.api.replacements.MethodSubstitutionRegistry;
40import org.graalvm.compiler.bytecode.BytecodeProvider;
41import org.graalvm.compiler.debug.Assertions;
42import org.graalvm.compiler.debug.GraalError;
43import org.graalvm.compiler.graph.Node;
44import org.graalvm.compiler.graph.iterators.NodeIterable;
45import org.graalvm.compiler.nodes.ValueNode;
46import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin.Receiver;
47import org.graalvm.util.EconomicMap;
48import org.graalvm.util.Equivalence;
49import org.graalvm.util.MapCursor;
50import org.graalvm.util.Pair;
51import org.graalvm.util.UnmodifiableEconomicMap;
52import org.graalvm.util.UnmodifiableMapCursor;
53
54import jdk.vm.ci.meta.MetaUtil;
55import jdk.vm.ci.meta.ResolvedJavaMethod;
56import jdk.vm.ci.meta.ResolvedJavaType;
57import jdk.vm.ci.meta.Signature;
58
59/**
60 * Manages a set of {@link InvocationPlugin}s.
61 *
62 * Most plugins are registered during initialization (i.e., before
63 * {@link #lookupInvocation(ResolvedJavaMethod)} or {@link #getBindings} is called). These
64 * registrations can be made with {@link Registration},
65 * {@link #register(InvocationPlugin, String, String, Type...)},
66 * {@link #register(InvocationPlugin, Type, String, Type...)} or
67 * {@link #registerOptional(InvocationPlugin, Type, String, Type...)}. Initialization is not
68 * thread-safe and so must only be performed by a single thread.
69 *
70 * Plugins that are not guaranteed to be made during initialization must use
71 * {@link LateRegistration}.
72 */
73public class InvocationPlugins {
74
75    public static class InvocationPluginReceiver implements InvocationPlugin.Receiver {
76        private final GraphBuilderContext parser;
77        private ValueNode[] args;
78        private ValueNode value;
79
80        public InvocationPluginReceiver(GraphBuilderContext parser) {
81            this.parser = parser;
82        }
83
84        @Override
85        public ValueNode get(boolean performNullCheck) {
86            assert args != null : "Cannot get the receiver of a static method";
87            if (!performNullCheck) {
88                return args[0];
89            }
90            if (value == null) {
91                value = parser.nullCheckedValue(args[0]);
92                if (value != args[0]) {
93                    args[0] = value;
94                }
95            }
96            return value;
97        }
98
99        @Override
100        public boolean isConstant() {
101            return args[0].isConstant();
102        }
103
104        public InvocationPluginReceiver init(ResolvedJavaMethod targetMethod, ValueNode[] newArgs) {
105            if (!targetMethod.isStatic()) {
106                this.args = newArgs;
107                this.value = null;
108                return this;
109            }
110            return null;
111        }
112    }
113
114    /**
115     * A symbol that is lazily {@linkplain OptionalLazySymbol#resolve() resolved} to a {@link Type}.
116     */
117    static class OptionalLazySymbol implements Type {
118        private static final Class<?> MASK_NULL = OptionalLazySymbol.class;
119        private final String name;
120        private Class<?> resolved;
121
122        OptionalLazySymbol(String name) {
123            this.name = name;
124        }
125
126        @Override
127        public String getTypeName() {
128            return name;
129        }
130
131        /**
132         * Gets the resolved {@link Class} corresponding to this symbol or {@code null} if
133         * resolution fails.
134         */
135        public Class<?> resolve() {
136            if (resolved == null) {
137                Class<?> resolvedOrNull = resolveClass(name, true);
138                resolved = resolvedOrNull == null ? MASK_NULL : resolvedOrNull;
139            }
140            return resolved == MASK_NULL ? null : resolved;
141        }
142
143        @Override
144        public String toString() {
145            return name;
146        }
147    }
148
149    /**
150     * Utility for {@linkplain InvocationPlugins#register(InvocationPlugin, Class, String, Class...)
151     * registration} of invocation plugins.
152     */
153    public static class Registration implements MethodSubstitutionRegistry {
154
155        private final InvocationPlugins plugins;
156        private final Type declaringType;
157        private final BytecodeProvider methodSubstitutionBytecodeProvider;
158        private boolean allowOverwrite;
159
160        @Override
161        public Class<?> getReceiverType() {
162            return Receiver.class;
163        }
164
165        /**
166         * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
167         * given class.
168         *
169         * @param plugins where to register the plugins
170         * @param declaringType the class declaring the methods for which plugins will be registered
171         *            via this object
172         */
173        public Registration(InvocationPlugins plugins, Type declaringType) {
174            this.plugins = plugins;
175            this.declaringType = declaringType;
176            this.methodSubstitutionBytecodeProvider = null;
177        }
178
179        /**
180         * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
181         * given class.
182         *
183         * @param plugins where to register the plugins
184         * @param declaringType the class declaring the methods for which plugins will be registered
185         *            via this object
186         * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
187         *            method substitutions
188         */
189        public Registration(InvocationPlugins plugins, Type declaringType, BytecodeProvider methodSubstitutionBytecodeProvider) {
190            this.plugins = plugins;
191            this.declaringType = declaringType;
192            this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
193        }
194
195        /**
196         * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
197         * given class.
198         *
199         * @param plugins where to register the plugins
200         * @param declaringClassName the name of the class class declaring the methods for which
201         *            plugins will be registered via this object
202         * @param methodSubstitutionBytecodeProvider provider used to get the bytecodes to parse for
203         *            method substitutions
204         */
205        public Registration(InvocationPlugins plugins, String declaringClassName, BytecodeProvider methodSubstitutionBytecodeProvider) {
206            this.plugins = plugins;
207            this.declaringType = new OptionalLazySymbol(declaringClassName);
208            this.methodSubstitutionBytecodeProvider = methodSubstitutionBytecodeProvider;
209        }
210
211        /**
212         * Configures this registration to allow or disallow overwriting of invocation plugins.
213         */
214        public Registration setAllowOverwrite(boolean allowOverwrite) {
215            this.allowOverwrite = allowOverwrite;
216            return this;
217        }
218
219        /**
220         * Registers a plugin for a method with no arguments.
221         *
222         * @param name the name of the method
223         * @param plugin the plugin to be registered
224         */
225        public void register0(String name, InvocationPlugin plugin) {
226            plugins.register(plugin, false, allowOverwrite, declaringType, name);
227        }
228
229        /**
230         * Registers a plugin for a method with 1 argument.
231         *
232         * @param name the name of the method
233         * @param plugin the plugin to be registered
234         */
235        public void register1(String name, Type arg, InvocationPlugin plugin) {
236            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg);
237        }
238
239        /**
240         * Registers a plugin for a method with 2 arguments.
241         *
242         * @param name the name of the method
243         * @param plugin the plugin to be registered
244         */
245        public void register2(String name, Type arg1, Type arg2, InvocationPlugin plugin) {
246            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2);
247        }
248
249        /**
250         * Registers a plugin for a method with 3 arguments.
251         *
252         * @param name the name of the method
253         * @param plugin the plugin to be registered
254         */
255        public void register3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) {
256            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3);
257        }
258
259        /**
260         * Registers a plugin for a method with 4 arguments.
261         *
262         * @param name the name of the method
263         * @param plugin the plugin to be registered
264         */
265        public void register4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
266            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
267        }
268
269        /**
270         * Registers a plugin for a method with 5 arguments.
271         *
272         * @param name the name of the method
273         * @param plugin the plugin to be registered
274         */
275        public void register5(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, InvocationPlugin plugin) {
276            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5);
277        }
278
279        /**
280         * Registers a plugin for a method with 6 arguments.
281         *
282         * @param name the name of the method
283         * @param plugin the plugin to be registered
284         */
285        public void register6(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, InvocationPlugin plugin) {
286            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6);
287        }
288
289        /**
290         * Registers a plugin for a method with 7 arguments.
291         *
292         * @param name the name of the method
293         * @param plugin the plugin to be registered
294         */
295        public void register7(String name, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7, InvocationPlugin plugin) {
296            plugins.register(plugin, false, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
297        }
298
299        /**
300         * Registers a plugin for an optional method with no arguments.
301         *
302         * @param name the name of the method
303         * @param plugin the plugin to be registered
304         */
305        public void registerOptional0(String name, InvocationPlugin plugin) {
306            plugins.register(plugin, true, allowOverwrite, declaringType, name);
307        }
308
309        /**
310         * Registers a plugin for an optional method with 1 argument.
311         *
312         * @param name the name of the method
313         * @param plugin the plugin to be registered
314         */
315        public void registerOptional1(String name, Type arg, InvocationPlugin plugin) {
316            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg);
317        }
318
319        /**
320         * Registers a plugin for an optional method with 2 arguments.
321         *
322         * @param name the name of the method
323         * @param plugin the plugin to be registered
324         */
325        public void registerOptional2(String name, Type arg1, Type arg2, InvocationPlugin plugin) {
326            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2);
327        }
328
329        /**
330         * Registers a plugin for an optional method with 3 arguments.
331         *
332         * @param name the name of the method
333         * @param plugin the plugin to be registered
334         */
335        public void registerOptional3(String name, Type arg1, Type arg2, Type arg3, InvocationPlugin plugin) {
336            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3);
337        }
338
339        /**
340         * Registers a plugin for an optional method with 4 arguments.
341         *
342         * @param name the name of the method
343         * @param plugin the plugin to be registered
344         */
345        public void registerOptional4(String name, Type arg1, Type arg2, Type arg3, Type arg4, InvocationPlugin plugin) {
346            plugins.register(plugin, true, allowOverwrite, declaringType, name, arg1, arg2, arg3, arg4);
347        }
348
349        /**
350         * Registers a plugin that implements a method based on the bytecode of a substitute method.
351         *
352         * @param substituteDeclaringClass the class declaring the substitute method
353         * @param name the name of both the original and substitute method
354         * @param argumentTypes the argument types of the method. Element 0 of this array must be
355         *            the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
356         *            is non-static. Upon returning, element 0 will have been rewritten to
357         *            {@code declaringClass}
358         */
359        @Override
360        public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, Type... argumentTypes) {
361            registerMethodSubstitution(substituteDeclaringClass, name, name, argumentTypes);
362        }
363
364        /**
365         * Registers a plugin that implements a method based on the bytecode of a substitute method.
366         *
367         * @param substituteDeclaringClass the class declaring the substitute method
368         * @param name the name of both the original method
369         * @param substituteName the name of the substitute method
370         * @param argumentTypes the argument types of the method. Element 0 of this array must be
371         *            the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
372         *            is non-static. Upon returning, element 0 will have been rewritten to
373         *            {@code declaringClass}
374         */
375        @Override
376        public void registerMethodSubstitution(Class<?> substituteDeclaringClass, String name, String substituteName, Type... argumentTypes) {
377            MethodSubstitutionPlugin plugin = createMethodSubstitution(substituteDeclaringClass, substituteName, argumentTypes);
378            plugins.register(plugin, false, allowOverwrite, declaringType, name, argumentTypes);
379        }
380
381        public MethodSubstitutionPlugin createMethodSubstitution(Class<?> substituteDeclaringClass, String substituteName, Type... argumentTypes) {
382            assert methodSubstitutionBytecodeProvider != null : "Registration used for method substitutions requires a non-null methodSubstitutionBytecodeProvider";
383            MethodSubstitutionPlugin plugin = new MethodSubstitutionPlugin(methodSubstitutionBytecodeProvider, substituteDeclaringClass, substituteName, argumentTypes);
384            return plugin;
385        }
386
387    }
388
389    /**
390     * Utility for registering plugins after Graal may have been initialized. Registrations made via
391     * this class are not finalized until {@link #close} is called.
392     */
393    public static class LateRegistration implements AutoCloseable {
394
395        private InvocationPlugins plugins;
396        private final List<Binding> bindings = new ArrayList<>();
397        private final Type declaringType;
398
399        /**
400         * Creates an object for registering {@link InvocationPlugin}s for methods declared by a
401         * given class.
402         *
403         * @param plugins where to register the plugins
404         * @param declaringType the class declaring the methods for which plugins will be registered
405         *            via this object
406         */
407        public LateRegistration(InvocationPlugins plugins, Type declaringType) {
408            this.plugins = plugins;
409            this.declaringType = declaringType;
410        }
411
412        /**
413         * Registers an invocation plugin for a given method. There must be no plugin currently
414         * registered for {@code method}.
415         *
416         * @param argumentTypes the argument types of the method. Element 0 of this array must be
417         *            the {@link Class} value for {@link InvocationPlugin.Receiver} iff the method
418         *            is non-static. Upon returning, element 0 will have been rewritten to
419         *            {@code declaringClass}
420         */
421        public void register(InvocationPlugin plugin, String name, Type... argumentTypes) {
422            boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
423            if (!isStatic) {
424                argumentTypes[0] = declaringType;
425            }
426
427            assert isStatic || argumentTypes[0] == declaringType;
428            Binding binding = new Binding(plugin, isStatic, name, argumentTypes);
429            bindings.add(binding);
430
431            assert Checks.check(this.plugins, declaringType, binding);
432            assert Checks.checkResolvable(false, declaringType, binding);
433        }
434
435        @Override
436        public void close() {
437            assert plugins != null : String.format("Late registrations of invocation plugins for %s is already closed", declaringType);
438            plugins.registerLate(declaringType, bindings);
439            plugins = null;
440        }
441    }
442
443    /**
444     * Associates an {@link InvocationPlugin} with the details of a method it substitutes.
445     */
446    public static class Binding {
447        /**
448         * The plugin this binding is for.
449         */
450        public final InvocationPlugin plugin;
451
452        /**
453         * Specifies if the associated method is static.
454         */
455        public final boolean isStatic;
456
457        /**
458         * The name of the associated method.
459         */
460        public final String name;
461
462        /**
463         * A partial
464         * <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3">method
465         * descriptor</a> for the associated method. The descriptor includes enclosing {@code '('}
466         * and {@code ')'} characters but omits the return type suffix.
467         */
468        public final String argumentsDescriptor;
469
470        /**
471         * Link in a list of bindings.
472         */
473        private Binding next;
474
475        Binding(InvocationPlugin data, boolean isStatic, String name, Type... argumentTypes) {
476            this.plugin = data;
477            this.isStatic = isStatic;
478            this.name = name;
479            StringBuilder buf = new StringBuilder();
480            buf.append('(');
481            for (int i = isStatic ? 0 : 1; i < argumentTypes.length; i++) {
482                buf.append(MetaUtil.toInternalName(argumentTypes[i].getTypeName()));
483            }
484            buf.append(')');
485            this.argumentsDescriptor = buf.toString();
486            assert !name.equals("<init>") || !isStatic : this;
487        }
488
489        Binding(ResolvedJavaMethod resolved, InvocationPlugin data) {
490            this.plugin = data;
491            this.isStatic = resolved.isStatic();
492            this.name = resolved.getName();
493            Signature sig = resolved.getSignature();
494            String desc = sig.toMethodDescriptor();
495            assert desc.indexOf(')') != -1 : desc;
496            this.argumentsDescriptor = desc.substring(0, desc.indexOf(')') + 1);
497            assert !name.equals("<init>") || !isStatic : this;
498        }
499
500        @Override
501        public String toString() {
502            return name + argumentsDescriptor;
503        }
504    }
505
506    /**
507     * Plugin registrations for already resolved methods. If non-null, then {@link #registrations}
508     * is null and no further registrations can be made.
509     */
510    private final UnmodifiableEconomicMap<ResolvedJavaMethod, InvocationPlugin> resolvedRegistrations;
511
512    /**
513     * Map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form to the
514     * invocation plugin bindings for the class. Tf non-null, then {@link #resolvedRegistrations}
515     * will be null.
516     */
517    private final EconomicMap<String, ClassPlugins> registrations;
518
519    /**
520     * Deferred registrations as well as the guard for delimiting the initial registration phase.
521     * The guard uses double-checked locking which is why this field is {@code volatile}.
522     */
523    private volatile List<Runnable> deferredRegistrations = new ArrayList<>();
524
525    /**
526     * Adds a {@link Runnable} for doing registration deferred until the first time
527     * {@link #get(ResolvedJavaMethod)} or {@link #closeRegistration()} is called on this object.
528     */
529    public void defer(Runnable deferrable) {
530        assert deferredRegistrations != null : "registration is closed";
531        deferredRegistrations.add(deferrable);
532    }
533
534    /**
535     * Support for registering plugins once this object may be accessed by multiple threads.
536     */
537    private volatile LateClassPlugins lateRegistrations;
538
539    /**
540     * Per-class bindings.
541     */
542    static class ClassPlugins {
543
544        /**
545         * Maps method names to binding lists.
546         */
547        private final EconomicMap<String, Binding> bindings = EconomicMap.create(Equivalence.DEFAULT);
548
549        /**
550         * Gets the invocation plugin for a given method.
551         *
552         * @return the invocation plugin for {@code method} or {@code null}
553         */
554        InvocationPlugin get(ResolvedJavaMethod method) {
555            assert !method.isBridge();
556            Binding binding = bindings.get(method.getName());
557            while (binding != null) {
558                if (method.isStatic() == binding.isStatic) {
559                    if (method.getSignature().toMethodDescriptor().startsWith(binding.argumentsDescriptor)) {
560                        return binding.plugin;
561                    }
562                }
563                binding = binding.next;
564            }
565            return null;
566        }
567
568        public void register(Binding binding, boolean allowOverwrite) {
569            if (allowOverwrite) {
570                if (lookup(binding) != null) {
571                    register(binding);
572                    return;
573                }
574            } else {
575                assert lookup(binding) == null : "a value is already registered for " + binding;
576            }
577            register(binding);
578        }
579
580        InvocationPlugin lookup(Binding binding) {
581            Binding b = bindings.get(binding.name);
582            while (b != null) {
583                if (b.isStatic == binding.isStatic && b.argumentsDescriptor.equals(binding.argumentsDescriptor)) {
584                    return b.plugin;
585                }
586                b = b.next;
587            }
588            return null;
589        }
590
591        /**
592         * Registers {@code binding}.
593         */
594        void register(Binding binding) {
595            Binding head = bindings.get(binding.name);
596            assert binding.next == null;
597            binding.next = head;
598            bindings.put(binding.name, binding);
599        }
600    }
601
602    static class LateClassPlugins extends ClassPlugins {
603        static final String CLOSED_LATE_CLASS_PLUGIN = "-----";
604        private final String className;
605        private final LateClassPlugins next;
606
607        LateClassPlugins(LateClassPlugins next, String className) {
608            assert next == null || next.className != CLOSED_LATE_CLASS_PLUGIN : "Late registration of invocation plugins is closed";
609            this.next = next;
610            this.className = className;
611        }
612    }
613
614    /**
615     * Registers a binding of a method to an invocation plugin.
616     *
617     * @param plugin invocation plugin to be associated with the specified method
618     * @param isStatic specifies if the method is static
619     * @param declaringClass the class declaring the method
620     * @param name the name of the method
621     * @param argumentTypes the argument types of the method. Element 0 of this array must be
622     *            {@code declaringClass} iff the method is non-static.
623     * @return an object representing the method
624     */
625    Binding put(InvocationPlugin plugin, boolean isStatic, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
626        assert resolvedRegistrations == null : "registration is closed";
627        String internalName = MetaUtil.toInternalName(declaringClass.getTypeName());
628        assert isStatic || argumentTypes[0] == declaringClass;
629        assert deferredRegistrations != null : "initial registration is closed - use " + LateRegistration.class.getName() + " for late registrations";
630
631        ClassPlugins classPlugins = registrations.get(internalName);
632        if (classPlugins == null) {
633            classPlugins = new ClassPlugins();
634            registrations.put(internalName, classPlugins);
635        }
636        Binding binding = new Binding(plugin, isStatic, name, argumentTypes);
637        classPlugins.register(binding, allowOverwrite);
638        return binding;
639    }
640
641    InvocationPlugin get(ResolvedJavaMethod method) {
642        if (resolvedRegistrations != null) {
643            return resolvedRegistrations.get(method);
644        } else {
645            if (!method.isBridge()) {
646                ResolvedJavaType declaringClass = method.getDeclaringClass();
647                flushDeferrables();
648                String internalName = declaringClass.getName();
649                ClassPlugins classPlugins = registrations.get(internalName);
650                InvocationPlugin res = null;
651                if (classPlugins != null) {
652                    res = classPlugins.get(method);
653                }
654                if (res == null) {
655                    LateClassPlugins lcp = findLateClassPlugins(internalName);
656                    if (lcp != null) {
657                        res = lcp.get(method);
658                    }
659                }
660                if (res != null) {
661                    if (canBeIntrinsified(declaringClass)) {
662                        return res;
663                    }
664                }
665                if (testExtensions != null) {
666                    // Avoid the synchronization in the common case that there
667                    // are no test extensions.
668                    synchronized (this) {
669                        if (testExtensions != null) {
670                            List<Binding> bindings = testExtensions.get(internalName);
671                            if (bindings != null) {
672                                String name = method.getName();
673                                String descriptor = method.getSignature().toMethodDescriptor();
674                                for (Binding b : bindings) {
675                                    if (b.isStatic == method.isStatic() &&
676                                                    b.name.equals(name) &&
677                                                    descriptor.startsWith(b.argumentsDescriptor)) {
678                                        return b.plugin;
679                                    }
680                                }
681                            }
682                        }
683                    }
684                }
685            } else {
686                // Supporting plugins for bridge methods would require including
687                // the return type in the registered signature. Until needed,
688                // this extra complexity is best avoided.
689            }
690        }
691        return null;
692    }
693
694    /**
695     * Determines if methods in a given class can have invocation plugins.
696     *
697     * @param declaringClass the class to test
698     */
699    protected boolean canBeIntrinsified(ResolvedJavaType declaringClass) {
700        return true;
701    }
702
703    LateClassPlugins findLateClassPlugins(String internalClassName) {
704        for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) {
705            if (lcp.className.equals(internalClassName)) {
706                return lcp;
707            }
708        }
709        return null;
710    }
711
712    @SuppressWarnings("serial")
713    static class InvocationPlugRegistrationError extends GraalError {
714        InvocationPlugRegistrationError(Throwable cause) {
715            super(cause);
716        }
717    }
718
719    private void flushDeferrables() {
720        if (deferredRegistrations != null) {
721            synchronized (this) {
722                if (deferredRegistrations != null) {
723                    try {
724                        for (Runnable deferrable : deferredRegistrations) {
725                            deferrable.run();
726                        }
727                        deferredRegistrations = null;
728                    } catch (InvocationPlugRegistrationError t) {
729                        throw t;
730                    } catch (Throwable t) {
731                        /*
732                         * Something went wrong during registration but it's possible we'll end up
733                         * coming back into this code. nulling out deferredRegistrations would just
734                         * cause other things to break and rerunning them would cause errors about
735                         * already registered plugins, so rethrow the original exception during
736                         * later invocations.
737                         */
738                        deferredRegistrations.clear();
739                        Runnable rethrow = new Runnable() {
740                            @Override
741                            public void run() {
742                                throw new InvocationPlugRegistrationError(t);
743                            }
744                        };
745                        deferredRegistrations.add(rethrow);
746                        rethrow.run();
747                    }
748                }
749            }
750        }
751    }
752
753    private volatile EconomicMap<String, List<Binding>> testExtensions;
754
755    private static int findBinding(List<Binding> list, Binding key) {
756        for (int i = 0; i < list.size(); i++) {
757            Binding b = list.get(i);
758            if (b.isStatic == key.isStatic && b.name.equals(key.name) && b.argumentsDescriptor.equals(key.argumentsDescriptor)) {
759                return i;
760            }
761        }
762        return -1;
763    }
764
765    /**
766     * Extends the plugins in this object with those from {@code other}. The added plugins should be
767     * {@linkplain #removeTestPlugins(InvocationPlugins) removed} after the test.
768     *
769     * This extension mechanism exists only for tests that want to add extra invocation plugins
770     * after the compiler has been initialized.
771     *
772     * @param ignored if non-null, the bindings from {@code other} already in this object prior to
773     *            calling this method are added to this list. These bindings are not added to this
774     *            object.
775     */
776    public synchronized void addTestPlugins(InvocationPlugins other, List<Pair<String, Binding>> ignored) {
777        assert resolvedRegistrations == null : "registration is closed";
778        EconomicMap<String, List<Binding>> otherBindings = other.getBindings(true, false);
779        if (otherBindings.isEmpty()) {
780            return;
781        }
782        if (testExtensions == null) {
783            testExtensions = EconomicMap.create();
784        }
785        MapCursor<String, List<Binding>> c = otherBindings.getEntries();
786        while (c.advance()) {
787            String declaringClass = c.getKey();
788            List<Binding> bindings = testExtensions.get(declaringClass);
789            if (bindings == null) {
790                bindings = new ArrayList<>();
791                testExtensions.put(declaringClass, bindings);
792            }
793            for (Binding b : c.getValue()) {
794                int index = findBinding(bindings, b);
795                if (index != -1) {
796                    if (ignored != null) {
797                        ignored.add(Pair.create(declaringClass, b));
798                    }
799                } else {
800                    bindings.add(b);
801                }
802            }
803        }
804    }
805
806    /**
807     * Removes the plugins from {@code other} in this object that were added by
808     * {@link #addTestPlugins}.
809     */
810    public synchronized void removeTestPlugins(InvocationPlugins other) {
811        assert resolvedRegistrations == null : "registration is closed";
812        if (testExtensions != null) {
813            MapCursor<String, List<Binding>> c = other.getBindings(false).getEntries();
814            while (c.advance()) {
815                String declaringClass = c.getKey();
816                List<Binding> bindings = testExtensions.get(declaringClass);
817                if (bindings != null) {
818                    for (Binding b : c.getValue()) {
819                        int index = findBinding(bindings, b);
820                        if (index != -1) {
821                            bindings.remove(index);
822                        }
823                    }
824                    if (bindings.isEmpty()) {
825                        testExtensions.removeKey(declaringClass);
826                    }
827                }
828            }
829            if (testExtensions.isEmpty()) {
830                testExtensions = null;
831            }
832        }
833    }
834
835    synchronized void registerLate(Type declaringType, List<Binding> bindings) {
836        String internalName = MetaUtil.toInternalName(declaringType.getTypeName());
837        assert findLateClassPlugins(internalName) == null : "Cannot have more than one late registration of invocation plugins for " + internalName;
838        LateClassPlugins lateClassPlugins = new LateClassPlugins(lateRegistrations, internalName);
839        for (Binding b : bindings) {
840            lateClassPlugins.register(b);
841        }
842        lateRegistrations = lateClassPlugins;
843    }
844
845    private synchronized boolean closeLateRegistrations() {
846        if (lateRegistrations == null || lateRegistrations.className != CLOSED_LATE_CLASS_PLUGIN) {
847            lateRegistrations = new LateClassPlugins(lateRegistrations, CLOSED_LATE_CLASS_PLUGIN);
848        }
849        return true;
850    }
851
852    /**
853     * Processes deferred registrations and then closes this object for future registration.
854     */
855    public void closeRegistration() {
856        assert closeLateRegistrations();
857        flushDeferrables();
858    }
859
860    public boolean isEmpty() {
861        if (resolvedRegistrations != null) {
862            return resolvedRegistrations.isEmpty();
863        }
864        return registrations.size() == 0 && lateRegistrations == null;
865    }
866
867    /**
868     * The plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched} before searching in
869     * this object.
870     */
871    protected final InvocationPlugins parent;
872
873    /**
874     * Creates a set of invocation plugins with no parent.
875     */
876    public InvocationPlugins() {
877        this(null);
878    }
879
880    /**
881     * Creates a set of invocation plugins.
882     *
883     * @param parent if non-null, this object will be searched first when looking up plugins
884     */
885    public InvocationPlugins(InvocationPlugins parent) {
886        InvocationPlugins p = parent;
887        this.parent = p;
888        this.registrations = EconomicMap.create();
889        this.resolvedRegistrations = null;
890    }
891
892    /**
893     * Creates a closed set of invocation plugins for a set of resolved methods. Such an object
894     * cannot have further plugins registered.
895     */
896    public InvocationPlugins(Map<ResolvedJavaMethod, InvocationPlugin> plugins, InvocationPlugins parent) {
897        this.parent = parent;
898        this.registrations = null;
899        this.deferredRegistrations = null;
900        EconomicMap<ResolvedJavaMethod, InvocationPlugin> map = EconomicMap.create(plugins.size());
901
902        for (Map.Entry<ResolvedJavaMethod, InvocationPlugin> entry : plugins.entrySet()) {
903            map.put(entry.getKey(), entry.getValue());
904        }
905        this.resolvedRegistrations = map;
906    }
907
908    protected void register(InvocationPlugin plugin, boolean isOptional, boolean allowOverwrite, Type declaringClass, String name, Type... argumentTypes) {
909        boolean isStatic = argumentTypes.length == 0 || argumentTypes[0] != InvocationPlugin.Receiver.class;
910        if (!isStatic) {
911            argumentTypes[0] = declaringClass;
912        }
913        Binding binding = put(plugin, isStatic, allowOverwrite, declaringClass, name, argumentTypes);
914        assert Checks.check(this, declaringClass, binding);
915        assert Checks.checkResolvable(isOptional, declaringClass, binding);
916    }
917
918    /**
919     * Registers an invocation plugin for a given method. There must be no plugin currently
920     * registered for {@code method}.
921     *
922     * @param argumentTypes the argument types of the method. Element 0 of this array must be the
923     *            {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is
924     *            non-static. Upon returning, element 0 will have been rewritten to
925     *            {@code declaringClass}
926     */
927    public void register(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
928        register(plugin, false, false, declaringClass, name, argumentTypes);
929    }
930
931    public void register(InvocationPlugin plugin, String declaringClass, String name, Type... argumentTypes) {
932        register(plugin, false, false, new OptionalLazySymbol(declaringClass), name, argumentTypes);
933    }
934
935    /**
936     * Registers an invocation plugin for a given, optional method. There must be no plugin
937     * currently registered for {@code method}.
938     *
939     * @param argumentTypes the argument types of the method. Element 0 of this array must be the
940     *            {@link Class} value for {@link InvocationPlugin.Receiver} iff the method is
941     *            non-static. Upon returning, element 0 will have been rewritten to
942     *            {@code declaringClass}
943     */
944    public void registerOptional(InvocationPlugin plugin, Type declaringClass, String name, Type... argumentTypes) {
945        register(plugin, true, false, declaringClass, name, argumentTypes);
946    }
947
948    /**
949     * Gets the plugin for a given method.
950     *
951     * @param method the method to lookup
952     * @return the plugin associated with {@code method} or {@code null} if none exists
953     */
954    public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
955        if (parent != null) {
956            InvocationPlugin plugin = parent.lookupInvocation(method);
957            if (plugin != null) {
958                return plugin;
959            }
960        }
961        return get(method);
962    }
963
964    /**
965     * Gets the set of registered invocation plugins.
966     *
967     * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
968     *         to the invocation plugin bindings for methods in the class
969     */
970    public EconomicMap<String, List<Binding>> getBindings(boolean includeParents) {
971        return getBindings(includeParents, true);
972    }
973
974    /**
975     * Gets the set of registered invocation plugins.
976     *
977     * @return a map from class names in {@linkplain MetaUtil#toInternalName(String) internal} form
978     *         to the invocation plugin bindings for methods in the class
979     */
980    private EconomicMap<String, List<Binding>> getBindings(boolean includeParents, boolean flushDeferrables) {
981        EconomicMap<String, List<Binding>> res = EconomicMap.create(Equivalence.DEFAULT);
982        if (parent != null && includeParents) {
983            res.putAll(parent.getBindings(true, flushDeferrables));
984        }
985        if (resolvedRegistrations != null) {
986            UnmodifiableMapCursor<ResolvedJavaMethod, InvocationPlugin> cursor = resolvedRegistrations.getEntries();
987            while (cursor.advance()) {
988                ResolvedJavaMethod method = cursor.getKey();
989                InvocationPlugin plugin = cursor.getValue();
990                String type = method.getDeclaringClass().getName();
991                List<Binding> bindings = res.get(type);
992                if (bindings == null) {
993                    bindings = new ArrayList<>();
994                    res.put(type, bindings);
995                }
996                bindings.add(new Binding(method, plugin));
997            }
998        } else {
999            if (flushDeferrables) {
1000                flushDeferrables();
1001            }
1002            MapCursor<String, ClassPlugins> classes = registrations.getEntries();
1003            while (classes.advance()) {
1004                String type = classes.getKey();
1005                ClassPlugins cp = classes.getValue();
1006                collectBindingsTo(res, type, cp);
1007            }
1008            for (LateClassPlugins lcp = lateRegistrations; lcp != null; lcp = lcp.next) {
1009                String type = lcp.className;
1010                collectBindingsTo(res, type, lcp);
1011            }
1012            if (testExtensions != null) {
1013                // Avoid the synchronization in the common case that there
1014                // are no test extensions.
1015                synchronized (this) {
1016                    if (testExtensions != null) {
1017                        MapCursor<String, List<Binding>> c = testExtensions.getEntries();
1018                        while (c.advance()) {
1019                            String name = c.getKey();
1020                            List<Binding> bindings = res.get(name);
1021                            if (bindings == null) {
1022                                bindings = new ArrayList<>();
1023                                res.put(name, bindings);
1024                            }
1025                            bindings.addAll(c.getValue());
1026                        }
1027                    }
1028                }
1029            }
1030        }
1031        return res;
1032    }
1033
1034    private static void collectBindingsTo(EconomicMap<String, List<Binding>> res, String type, ClassPlugins cp) {
1035        MapCursor<String, Binding> methods = cp.bindings.getEntries();
1036        while (methods.advance()) {
1037            List<Binding> bindings = res.get(type);
1038            if (bindings == null) {
1039                bindings = new ArrayList<>();
1040                res.put(type, bindings);
1041            }
1042            for (Binding b = methods.getValue(); b != null; b = b.next) {
1043                bindings.add(b);
1044            }
1045        }
1046    }
1047
1048    /**
1049     * Gets the invocation plugins {@linkplain #lookupInvocation(ResolvedJavaMethod) searched}
1050     * before searching in this object.
1051     */
1052    public InvocationPlugins getParent() {
1053        return parent;
1054    }
1055
1056    @Override
1057    public String toString() {
1058        UnmodifiableMapCursor<String, List<Binding>> entries = getBindings(false, false).getEntries();
1059        List<String> all = new ArrayList<>();
1060        while (entries.advance()) {
1061            String c = MetaUtil.internalNameToJava(entries.getKey(), true, false);
1062            for (Binding b : entries.getValue()) {
1063                all.add(c + '.' + b);
1064            }
1065        }
1066        Collections.sort(all);
1067        StringBuilder buf = new StringBuilder();
1068        String nl = String.format("%n");
1069        for (String s : all) {
1070            if (buf.length() != 0) {
1071                buf.append(nl);
1072            }
1073            buf.append(s);
1074        }
1075        if (parent != null) {
1076            if (buf.length() != 0) {
1077                buf.append(nl);
1078            }
1079            buf.append("// parent").append(nl).append(parent);
1080        }
1081        return buf.toString();
1082    }
1083
1084    /**
1085     * Code only used in assertions. Putting this in a separate class reduces class load time.
1086     */
1087    private static class Checks {
1088        private static final int MAX_ARITY = 7;
1089        /**
1090         * The set of all {@link InvocationPlugin#apply} method signatures.
1091         */
1092        static final Class<?>[][] SIGS;
1093
1094        static {
1095            if (!Assertions.assertionsEnabled()) {
1096                throw new GraalError("%s must only be used in assertions", Checks.class.getName());
1097            }
1098            ArrayList<Class<?>[]> sigs = new ArrayList<>(MAX_ARITY);
1099            for (Method method : InvocationPlugin.class.getDeclaredMethods()) {
1100                if (!Modifier.isStatic(method.getModifiers()) && method.getName().equals("apply")) {
1101                    Class<?>[] sig = method.getParameterTypes();
1102                    assert sig[0] == GraphBuilderContext.class;
1103                    assert sig[1] == ResolvedJavaMethod.class;
1104                    assert sig[2] == InvocationPlugin.Receiver.class;
1105                    assert Arrays.asList(sig).subList(3, sig.length).stream().allMatch(c -> c == ValueNode.class);
1106                    while (sigs.size() < sig.length - 2) {
1107                        sigs.add(null);
1108                    }
1109                    sigs.set(sig.length - 3, sig);
1110                }
1111            }
1112            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),
1113                            ValueNode.class.getSimpleName());
1114            SIGS = sigs.toArray(new Class<?>[sigs.size()][]);
1115        }
1116
1117        static boolean containsBinding(InvocationPlugins p, Type declaringType, Binding key) {
1118            String internalName = MetaUtil.toInternalName(declaringType.getTypeName());
1119            ClassPlugins classPlugins = p.registrations.get(internalName);
1120            return classPlugins != null && classPlugins.lookup(key) != null;
1121        }
1122
1123        public static boolean check(InvocationPlugins plugins, Type declaringType, Binding binding) {
1124            InvocationPlugin plugin = binding.plugin;
1125            InvocationPlugins p = plugins.parent;
1126            while (p != null) {
1127                assert !containsBinding(p, declaringType, binding) : "a plugin is already registered for " + binding;
1128                p = p.parent;
1129            }
1130            if (plugin instanceof ForeignCallPlugin || plugin instanceof GeneratedInvocationPlugin) {
1131                return true;
1132            }
1133            if (plugin instanceof MethodSubstitutionPlugin) {
1134                MethodSubstitutionPlugin msplugin = (MethodSubstitutionPlugin) plugin;
1135                Method substitute = msplugin.getJavaSubstitute();
1136                assert substitute.getAnnotation(MethodSubstitution.class) != null : format("Substitute method must be annotated with @%s: %s", MethodSubstitution.class.getSimpleName(), substitute);
1137                return true;
1138            }
1139            int arguments = parseParameters(binding.argumentsDescriptor).size();
1140            assert arguments < SIGS.length : format("need to extend %s to support method with %d arguments: %s", InvocationPlugin.class.getSimpleName(), arguments, binding);
1141            for (Method m : plugin.getClass().getDeclaredMethods()) {
1142                if (m.getName().equals("apply")) {
1143                    Class<?>[] parameterTypes = m.getParameterTypes();
1144                    if (Arrays.equals(SIGS[arguments], parameterTypes)) {
1145                        return true;
1146                    }
1147                }
1148            }
1149            throw new AssertionError(format("graph builder plugin for %s not found", binding));
1150        }
1151
1152        static boolean checkResolvable(boolean isOptional, Type declaringType, Binding binding) {
1153            Class<?> declaringClass = InvocationPlugins.resolveType(declaringType, isOptional);
1154            if (declaringClass == null) {
1155                return true;
1156            }
1157            if (binding.name.equals("<init>")) {
1158                if (resolveConstructor(declaringClass, binding) == null && !isOptional) {
1159                    throw new AssertionError(String.format("Constructor not found: %s%s", declaringClass.getName(), binding.argumentsDescriptor));
1160                }
1161            } else {
1162                if (resolveMethod(declaringClass, binding) == null && !isOptional) {
1163                    throw new AssertionError(String.format("Method not found: %s.%s%s", declaringClass.getName(), binding.name, binding.argumentsDescriptor));
1164                }
1165            }
1166            return true;
1167        }
1168    }
1169
1170    /**
1171     * Checks a set of nodes added to the graph by an {@link InvocationPlugin}.
1172     *
1173     * @param b the graph builder that applied the plugin
1174     * @param plugin a plugin that was just applied
1175     * @param newNodes the nodes added to the graph by {@code plugin}
1176     * @throws AssertionError if any check fail
1177     */
1178    public void checkNewNodes(GraphBuilderContext b, InvocationPlugin plugin, NodeIterable<Node> newNodes) {
1179        if (parent != null) {
1180            parent.checkNewNodes(b, plugin, newNodes);
1181        }
1182    }
1183
1184    /**
1185     * Resolves a name to a class.
1186     *
1187     * @param className the name of the class to resolve
1188     * @param optional if true, resolution failure returns null
1189     * @return the resolved class or null if resolution fails and {@code optional} is true
1190     */
1191    public static Class<?> resolveClass(String className, boolean optional) {
1192        try {
1193            // Need to use the system class loader to handle classes
1194            // loaded by the application class loader which is not
1195            // delegated to by the JVMCI class loader.
1196            ClassLoader cl = ClassLoader.getSystemClassLoader();
1197            return Class.forName(className, false, cl);
1198        } catch (ClassNotFoundException e) {
1199            if (optional) {
1200                return null;
1201            }
1202            throw new GraalError("Could not resolve type " + className);
1203        }
1204    }
1205
1206    /**
1207     * Resolves a {@link Type} to a {@link Class}.
1208     *
1209     * @param type the type to resolve
1210     * @param optional if true, resolution failure returns null
1211     * @return the resolved class or null if resolution fails and {@code optional} is true
1212     */
1213    public static Class<?> resolveType(Type type, boolean optional) {
1214        if (type instanceof Class) {
1215            return (Class<?>) type;
1216        }
1217        if (type instanceof OptionalLazySymbol) {
1218            return ((OptionalLazySymbol) type).resolve();
1219        }
1220        return resolveClass(type.getTypeName(), optional);
1221    }
1222
1223    private static List<String> toInternalTypeNames(Class<?>[] types) {
1224        String[] res = new String[types.length];
1225        for (int i = 0; i < types.length; i++) {
1226            res[i] = MetaUtil.toInternalName(types[i].getTypeName());
1227        }
1228        return Arrays.asList(res);
1229    }
1230
1231    /**
1232     * Resolves a given binding to a method in a given class. If more than one method with the
1233     * parameter types matching {@code binding} is found and the return types of all the matching
1234     * methods form an inheritance chain, the one with the most specific type is returned; otherwise
1235     * {@link NoSuchMethodError} is thrown.
1236     *
1237     * @param declaringClass the class to search for a method matching {@code binding}
1238     * @return the method (if any) in {@code declaringClass} matching binding
1239     */
1240    public static Method resolveMethod(Class<?> declaringClass, Binding binding) {
1241        if (binding.name.equals("<init>")) {
1242            return null;
1243        }
1244        Method[] methods = declaringClass.getDeclaredMethods();
1245        List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor);
1246        for (int i = 0; i < methods.length; ++i) {
1247            Method m = methods[i];
1248            if (binding.isStatic == Modifier.isStatic(m.getModifiers()) && m.getName().equals(binding.name)) {
1249                if (parameterTypeNames.equals(toInternalTypeNames(m.getParameterTypes()))) {
1250                    for (int j = i + 1; j < methods.length; ++j) {
1251                        Method other = methods[j];
1252                        if (binding.isStatic == Modifier.isStatic(other.getModifiers()) && other.getName().equals(binding.name)) {
1253                            if (parameterTypeNames.equals(toInternalTypeNames(other.getParameterTypes()))) {
1254                                if (m.getReturnType().isAssignableFrom(other.getReturnType())) {
1255                                    // `other` has a more specific return type - choose it
1256                                    // (m is most likely a bridge method)
1257                                    m = other;
1258                                } else {
1259                                    if (!other.getReturnType().isAssignableFrom(m.getReturnType())) {
1260                                        throw new NoSuchMethodError(String.format(
1261                                                        "Found 2 methods with same name and parameter types but unrelated return types:%n %s%n %s", m, other));
1262                                    }
1263                                }
1264                            }
1265                        }
1266                    }
1267                    return m;
1268                }
1269            }
1270        }
1271        return null;
1272    }
1273
1274    /**
1275     * Resolves a given binding to a constructor in a given class.
1276     *
1277     * @param declaringClass the class to search for a constructor matching {@code binding}
1278     * @return the constructor (if any) in {@code declaringClass} matching binding
1279     */
1280    public static Constructor<?> resolveConstructor(Class<?> declaringClass, Binding binding) {
1281        if (!binding.name.equals("<init>")) {
1282            return null;
1283        }
1284        Constructor<?>[] constructors = declaringClass.getDeclaredConstructors();
1285        List<String> parameterTypeNames = parseParameters(binding.argumentsDescriptor);
1286        for (int i = 0; i < constructors.length; ++i) {
1287            Constructor<?> c = constructors[i];
1288            if (parameterTypeNames.equals(toInternalTypeNames(c.getParameterTypes()))) {
1289                return c;
1290            }
1291        }
1292        return null;
1293    }
1294
1295    private static List<String> parseParameters(String argumentsDescriptor) {
1296        assert argumentsDescriptor.startsWith("(") && argumentsDescriptor.endsWith(")") : argumentsDescriptor;
1297        List<String> res = new ArrayList<>();
1298        int cur = 1;
1299        int end = argumentsDescriptor.length() - 1;
1300        while (cur != end) {
1301            char first;
1302            int start = cur;
1303            do {
1304                first = argumentsDescriptor.charAt(cur++);
1305            } while (first == '[');
1306
1307            switch (first) {
1308                case 'L':
1309                    int endObject = argumentsDescriptor.indexOf(';', cur);
1310                    if (endObject == -1) {
1311                        throw new GraalError("Invalid object type at index %d in signature: %s", cur, argumentsDescriptor);
1312                    }
1313                    cur = endObject + 1;
1314                    break;
1315                case 'V':
1316                case 'I':
1317                case 'B':
1318                case 'C':
1319                case 'D':
1320                case 'F':
1321                case 'J':
1322                case 'S':
1323                case 'Z':
1324                    break;
1325                default:
1326                    throw new GraalError("Invalid character at index %d in signature: %s", cur, argumentsDescriptor);
1327            }
1328            res.add(argumentsDescriptor.substring(start, cur));
1329        }
1330        return res;
1331    }
1332}
1333