AbstractJavaLinker.java revision 1805:7caf1f762f1d
1/*
2 * Copyright (c) 2010, 2013, 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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * This file is available under and governed by the GNU General Public
28 * License version 2 only, as published by the Free Software Foundation.
29 * However, the following notice accompanied the original version of this
30 * file, and Oracle licenses the original version of this file under the BSD
31 * license:
32 */
33/*
34   Copyright 2009-2013 Attila Szegedi
35
36   Licensed under both the Apache License, Version 2.0 (the "Apache License")
37   and the BSD License (the "BSD License"), with licensee being free to
38   choose either of the two at their discretion.
39
40   You may not use this file except in compliance with either the Apache
41   License or the BSD License.
42
43   If you choose to use this file in compliance with the Apache License, the
44   following notice applies to you:
45
46       You may obtain a copy of the Apache License at
47
48           http://www.apache.org/licenses/LICENSE-2.0
49
50       Unless required by applicable law or agreed to in writing, software
51       distributed under the License is distributed on an "AS IS" BASIS,
52       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
53       implied. See the License for the specific language governing
54       permissions and limitations under the License.
55
56   If you choose to use this file in compliance with the BSD License, the
57   following notice applies to you:
58
59       Redistribution and use in source and binary forms, with or without
60       modification, are permitted provided that the following conditions are
61       met:
62       * Redistributions of source code must retain the above copyright
63         notice, this list of conditions and the following disclaimer.
64       * Redistributions in binary form must reproduce the above copyright
65         notice, this list of conditions and the following disclaimer in the
66         documentation and/or other materials provided with the distribution.
67       * Neither the name of the copyright holder nor the names of
68         contributors may be used to endorse or promote products derived from
69         this software without specific prior written permission.
70
71       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
72       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
73       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
74       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
75       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
76       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
77       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
78       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
79       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
80       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
81       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
82*/
83
84package jdk.dynalink.beans;
85
86import java.lang.invoke.MethodHandle;
87import java.lang.invoke.MethodHandles;
88import java.lang.invoke.MethodType;
89import java.lang.reflect.AccessibleObject;
90import java.lang.reflect.Constructor;
91import java.lang.reflect.Field;
92import java.lang.reflect.Member;
93import java.lang.reflect.Method;
94import java.lang.reflect.Modifier;
95import java.util.Arrays;
96import java.util.Collections;
97import java.util.HashMap;
98import java.util.List;
99import java.util.Map;
100import java.util.Set;
101import jdk.dynalink.CallSiteDescriptor;
102import jdk.dynalink.NamedOperation;
103import jdk.dynalink.Namespace;
104import jdk.dynalink.NamespaceOperation;
105import jdk.dynalink.Operation;
106import jdk.dynalink.StandardNamespace;
107import jdk.dynalink.StandardOperation;
108import jdk.dynalink.beans.GuardedInvocationComponent.ValidationType;
109import jdk.dynalink.internal.InternalTypeUtilities;
110import jdk.dynalink.linker.GuardedInvocation;
111import jdk.dynalink.linker.GuardingDynamicLinker;
112import jdk.dynalink.linker.LinkRequest;
113import jdk.dynalink.linker.LinkerServices;
114import jdk.dynalink.linker.support.Guards;
115import jdk.dynalink.linker.support.Lookup;
116import jdk.internal.reflect.CallerSensitive;
117
118/**
119 * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
120 * exposure and method calls for both static and instance facets of a class.
121 */
122abstract class AbstractJavaLinker implements GuardingDynamicLinker {
123
124    final Class<?> clazz;
125    private final MethodHandle classGuard;
126    private final MethodHandle assignableGuard;
127    private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();
128    private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
129    private final Map<String, DynamicMethod> methods = new HashMap<>();
130
131    AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) {
132        this(clazz, classGuard, classGuard);
133    }
134
135    AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) {
136        this.clazz = clazz;
137        this.classGuard = classGuard;
138        this.assignableGuard = assignableGuard;
139
140        final FacetIntrospector introspector = createFacetIntrospector();
141        // Add methods and properties
142        for(final Method method: introspector.getMethods()) {
143            final String name = method.getName();
144            // Add method
145            addMember(name, method, methods);
146            // Add the method as a property getter and/or setter
147            if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
148                // Property getter
149                setPropertyGetter(method, 3);
150            } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
151                    method.getReturnType() == boolean.class) {
152                // Boolean property getter
153                setPropertyGetter(method, 2);
154            } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
155                // Property setter
156                addMember(decapitalize(name.substring(3)), method, propertySetters);
157            }
158        }
159
160        // Add field getter/setters as property getters/setters.
161        for(final Field field: introspector.getFields()) {
162            final String name = field.getName();
163            // Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
164            if(!propertyGetters.containsKey(name)) {
165                setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
166            }
167            if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
168                addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),
169                        propertySetters);
170            }
171        }
172
173        // Add inner classes, but only those for which we don't hide a property with it
174        for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
175            final String name = innerClassSpec.getKey();
176            if(!propertyGetters.containsKey(name)) {
177                setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
178            }
179        }
180    }
181
182    private static String decapitalize(final String str) {
183        assert str != null;
184        if(str.isEmpty()) {
185            return str;
186        }
187
188        final char c0 = str.charAt(0);
189        if(Character.isLowerCase(c0)) {
190            return str;
191        }
192
193        // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize
194        if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {
195            return str;
196        }
197
198        final char c[] = str.toCharArray();
199        c[0] = Character.toLowerCase(c0);
200        return new String(c);
201    }
202
203    abstract FacetIntrospector createFacetIntrospector();
204
205    Set<String> getReadablePropertyNames() {
206        return getUnmodifiableKeys(propertyGetters);
207    }
208
209    Set<String> getWritablePropertyNames() {
210        return getUnmodifiableKeys(propertySetters);
211    }
212
213    Set<String> getMethodNames() {
214        return getUnmodifiableKeys(methods);
215    }
216
217    private static Set<String> getUnmodifiableKeys(final Map<String, ?> m) {
218        return Collections.unmodifiableSet(m.keySet());
219    }
220
221    /**
222     * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
223     * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
224     * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
225     * instead.
226     * @param name name of the property
227     * @param handle the method handle that implements the property getter
228     * @param validationType the validation type for the property
229     */
230    private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) {
231        propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
232    }
233
234    /**
235     * Sets the specified reflective method to be the property getter for the specified property.
236     * @param getter the getter method
237     * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
238     * names starting with "is".
239     */
240    private void setPropertyGetter(final Method getter, final int prefixLen) {
241        setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
242                getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
243    }
244
245    /**
246     * Sets the specified method handle to be the property getter for the specified property. Note that you can only
247     * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
248     * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
249     * instead.
250     * @param name name of the property
251     * @param handle the method handle that implements the property getter
252     * @param validationType the validation type for the property
253     */
254    void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) {
255        setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
256    }
257
258    private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) {
259        addMember(name, createDynamicMethod(ao), methodMap);
260    }
261
262    private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) {
263        final DynamicMethod existingMethod = methodMap.get(name);
264        final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
265        if(newMethod != existingMethod) {
266            methodMap.put(name, newMethod);
267        }
268    }
269
270    /**
271     * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The
272     * methods should represent all overloads of the same name (or all constructors of the class).
273     * @param members the reflective members
274     * @param clazz the class declaring the reflective members
275     * @param name the common name of the reflective members.
276     * @return a dynamic method representing all the specified reflective members.
277     */
278    static DynamicMethod createDynamicMethod(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) {
279        DynamicMethod dynMethod = null;
280        for(final AccessibleObject method: members) {
281            dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
282        }
283        return dynMethod;
284    }
285
286    /**
287     * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will
288     * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive
289     * dynamic method when needed.
290     * @param m the reflective member
291     * @return the single dynamic method representing the reflective member
292     */
293    private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
294        if (m.isAnnotationPresent(CallerSensitive.class)) {
295            // Method has @CallerSensitive annotation
296            return new CallerSensitiveDynamicMethod(m);
297        }
298        // Method has no @CallerSensitive annotation
299        final MethodHandle mh;
300        try {
301            mh = unreflectSafely(m);
302        } catch (final IllegalAccessError e) {
303            // java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
304            // marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
305            return new CallerSensitiveDynamicMethod(m);
306        }
307        // Proceed with non-caller sensitive
308        final Member member = (Member)m;
309        return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
310    }
311
312    /**
313     * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be
314     * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were
315     * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege
316     * unreflector as its caller, and thus completely useless.
317     * @param m the method or constructor
318     * @return the method handle
319     */
320    private static MethodHandle unreflectSafely(final AccessibleObject m) {
321        if(m instanceof Method) {
322            final Method reflMethod = (Method)m;
323            final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);
324            if(Modifier.isStatic(reflMethod.getModifiers())) {
325                return StaticClassIntrospector.editStaticMethodHandle(handle);
326            }
327            return handle;
328        }
329        return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));
330    }
331
332    private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {
333        if(existing == null) {
334            return method;
335        } else if(existing.contains(method)) {
336            return existing;
337        } else if(existing instanceof SingleDynamicMethod) {
338            final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
339            odm.addMethod(((SingleDynamicMethod)existing));
340            odm.addMethod(method);
341            return odm;
342        } else if(existing instanceof OverloadedDynamicMethod) {
343            ((OverloadedDynamicMethod)existing).addMethod(method);
344            return existing;
345        }
346        throw new AssertionError();
347    }
348
349    @Override
350    public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
351            throws Exception {
352        final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor();
353
354        final MissingMemberHandlerFactory missingMemberHandlerFactory;
355        final LinkerServices directLinkerServices;
356        if (linkerServices instanceof LinkerServicesWithMissingMemberHandlerFactory) {
357            final LinkerServicesWithMissingMemberHandlerFactory lswmmhf = ((LinkerServicesWithMissingMemberHandlerFactory)linkerServices);
358            missingMemberHandlerFactory = lswmmhf.missingMemberHandlerFactory;
359            directLinkerServices = lswmmhf.linkerServices;
360        } else {
361            missingMemberHandlerFactory = null;
362            directLinkerServices = linkerServices;
363        }
364
365        final GuardedInvocationComponent gic = getGuardedInvocationComponent(
366                new ComponentLinkRequest(request, directLinkerServices,
367                        missingMemberHandlerFactory));
368        return gic != null ? gic.getGuardedInvocation() : null;
369    }
370
371    static final class ComponentLinkRequest {
372        final LinkRequest linkRequest;
373        final LinkerServices linkerServices;
374        final MissingMemberHandlerFactory missingMemberHandlerFactory;
375        final Operation baseOperation;
376        final List<Namespace> namespaces;
377        final Object name;
378
379        ComponentLinkRequest(final LinkRequest linkRequest,
380                final LinkerServices linkerServices,
381                final MissingMemberHandlerFactory missingMemberHandlerFactory) {
382            this.linkRequest = linkRequest;
383            this.linkerServices = linkerServices;
384            this.missingMemberHandlerFactory = missingMemberHandlerFactory;
385            final Operation namedOp = linkRequest.getCallSiteDescriptor().getOperation();
386            this.name = NamedOperation.getName(namedOp);
387            final Operation namespaceOp = NamedOperation.getBaseOperation(namedOp);
388            this.baseOperation = NamespaceOperation.getBaseOperation(namespaceOp);
389            this.namespaces = Arrays.asList(NamespaceOperation.getNamespaces(namespaceOp));
390        }
391
392        private ComponentLinkRequest(final LinkRequest linkRequest,
393                final LinkerServices linkerServices,
394                final MissingMemberHandlerFactory missingMemberHandlerFactory,
395                final Operation baseOperation, final List<Namespace> namespaces, final Object name) {
396            this.linkRequest = linkRequest;
397            this.linkerServices = linkerServices;
398            this.missingMemberHandlerFactory = missingMemberHandlerFactory;
399            this.baseOperation = baseOperation;
400            this.namespaces = namespaces;
401            this.name = name;
402        }
403
404        CallSiteDescriptor getDescriptor() {
405            return linkRequest.getCallSiteDescriptor();
406        }
407
408        ComponentLinkRequest popNamespace() {
409            return new ComponentLinkRequest(linkRequest, linkerServices,
410                    missingMemberHandlerFactory, baseOperation,
411                namespaces.subList(1, namespaces.size()), name);
412        }
413    }
414
415    protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req)
416    throws Exception {
417        if (!req.namespaces.isEmpty()) {
418            final Namespace ns = req.namespaces.get(0);
419            final Operation op = req.baseOperation;
420            if (op == StandardOperation.GET) {
421                if (ns == StandardNamespace.PROPERTY) {
422                    return getPropertyGetter(req.popNamespace());
423                } else if (ns == StandardNamespace.METHOD) {
424                    return getMethodGetter(req.popNamespace());
425                }
426            } else if (op == StandardOperation.SET && ns == StandardNamespace.PROPERTY) {
427                return getPropertySetter(req.popNamespace());
428            }
429        }
430        return null;
431    }
432
433    GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception {
434        if (req.namespaces.isEmpty()) {
435            return createNoSuchMemberHandler(req.missingMemberHandlerFactory,
436                    req.linkRequest, req.linkerServices);
437        }
438        final GuardedInvocationComponent gic = getGuardedInvocationComponent(req);
439        if (gic != null) {
440            return gic;
441        }
442        return getNextComponent(req.popNamespace());
443    }
444
445    private GuardedInvocationComponent createNoSuchMemberHandler(
446            final MissingMemberHandlerFactory missingMemberHandlerFactory,
447            final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
448        if (missingMemberHandlerFactory == null) {
449            return null;
450        }
451        final MethodHandle handler = missingMemberHandlerFactory.createMissingMemberHandler(linkRequest, linkerServices);
452        if (handler == null) {
453            return null;
454        }
455        final MethodType type = linkRequest.getCallSiteDescriptor().getMethodType();
456        // The returned handler is allowed to differ in return type.
457        assert handler.type().changeReturnType(type.returnType()).equals(type);
458        return getClassGuardedInvocationComponent(handler, type);
459    }
460
461    static final <T> List<T> pop(final List<T> l) {
462        return l.subList(1, l.size());
463    }
464
465    MethodHandle getClassGuard(final CallSiteDescriptor desc) {
466        return getClassGuard(desc.getMethodType());
467    }
468
469    MethodHandle getClassGuard(final MethodType type) {
470        return Guards.asType(classGuard, type);
471    }
472
473    GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {
474        return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
475    }
476
477    abstract SingleDynamicMethod getConstructorMethod(final String signature);
478
479    private MethodHandle getAssignableGuard(final MethodType type) {
480        return Guards.asType(assignableGuard, type);
481    }
482
483    private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
484            final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
485        final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
486        return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
487    }
488
489    private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
490            final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
491        final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
492        return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
493    }
494
495    private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
496        final DynamicMethod dynaMethod = methodMap.get(methodName);
497        return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
498    }
499
500    private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName,
501            final Map<String, DynamicMethod> methodsMap) {
502        // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
503        // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
504        // resolution works correctly in almost every situation. However, in presence of many language-specific
505        // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
506        // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
507        // for performance reasons.
508
509        // Is the method name lexically of the form "name(types)"?
510        final int lastChar = fullName.length() - 1;
511        if(fullName.charAt(lastChar) != ')') {
512            return null;
513        }
514        final int openBrace = fullName.indexOf('(');
515        if(openBrace == -1) {
516            return null;
517        }
518
519        final String name = fullName.substring(0, openBrace);
520        final String signature = fullName.substring(openBrace + 1, lastChar);
521
522        // Find an existing method for the "name" part
523        final DynamicMethod simpleNamedMethod = methodsMap.get(name);
524        if(simpleNamedMethod == null) {
525            // explicit signature constructor access
526            // Java.type("java.awt.Color")["(int,int,int)"]
527            // will get Color(int,int,int) constructor of Color class.
528            if (name.isEmpty()) {
529                return getConstructorMethod(signature);
530            }
531
532            return null;
533        }
534
535        // Try to get a narrowed dynamic method for the explicit parameter types.
536        return simpleNamedMethod.getMethodForExactParamTypes(signature);
537    }
538
539    private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
540            boolean.class, MethodHandle.class));
541    private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
542            MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
543
544    private GuardedInvocationComponent getPropertySetter(final ComponentLinkRequest req) throws Exception {
545        if (req.name == null) {
546            return getUnnamedPropertySetter(req);
547        }
548        return getNamedPropertySetter(req);
549    }
550
551    private GuardedInvocationComponent getUnnamedPropertySetter(final ComponentLinkRequest req) throws Exception {
552        final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
553        // Must have three arguments: target object, property name, and property value.
554        assertParameterCount(callSiteDescriptor, 3);
555
556        // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
557        // valid for us to convert return values proactively. Also, since we don't know what setters will be
558        // invoked, we'll conservatively presume Object return type. The one exception is void return.
559        final MethodType origType = callSiteDescriptor.getMethodType();
560        final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);
561        final LinkerServices linkerServices = req.linkerServices;
562
563        // What's below is basically:
564        //   foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
565        //     get_setter_handle(type, linkerServices))
566        // only with a bunch of method signature adjustments. Basically, retrieve method setter
567        // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
568        // component's invocation.
569
570        // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
571        // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
572        // Object return type).
573        final MethodType setterType = type.dropParameterTypes(1, 2);
574        // Bind property setter handle to the expected setter type and linker services. Type is
575        // MethodHandle(Object, String, Object)
576        final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
577                callSiteDescriptor.changeMethodType(setterType), linkerServices);
578
579        // Cast getter to MethodHandle(O, N, V)
580        final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
581                MethodHandle.class));
582
583        // Handle to invoke the setter R(MethodHandle, O, V)
584        final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
585        // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
586        final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
587                1));
588        final GuardedInvocationComponent nextComponent = getNextComponent(req);
589
590        final MethodHandle fallbackFolded;
591        if (nextComponent == null) {
592            // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
593            fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
594                    type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
595        } else {
596            // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
597            // extra argument resulting from fold
598            fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
599                    0, MethodHandle.class);
600        }
601
602        // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
603        final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
604                    IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
605        if(nextComponent == null) {
606            return getClassGuardedInvocationComponent(compositeSetter, type);
607        }
608        return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
609    }
610
611    private GuardedInvocationComponent getNamedPropertySetter(final ComponentLinkRequest req) throws Exception {
612        final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
613        // Must have two arguments: target object and property value
614        assertParameterCount(callSiteDescriptor, 2);
615        final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, req.linkerServices,
616                req.name.toString(), propertySetters);
617        // If we have a property setter with this name, this composite operation will always stop here
618        if(gi != null) {
619            return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
620        }
621        // If we don't have a property setter with this name, always fall back to the next namespace (if any).
622        return getNextComponent(req);
623    }
624
625    private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
626
627    private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
628            boolean.class, AnnotatedDynamicMethod.class));
629    private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
630            MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
631    private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
632            "getTarget", MethodType.methodType(MethodHandle.class, CallSiteDescriptor.class, LinkerServices.class));
633    private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
634
635    private GuardedInvocationComponent getPropertyGetter(final ComponentLinkRequest req) throws Exception {
636        if (req.name == null) {
637            return getUnnamedPropertyGetter(req);
638        }
639        return getNamedPropertyGetter(req);
640    }
641
642    private GuardedInvocationComponent getUnnamedPropertyGetter(final ComponentLinkRequest req) throws Exception {
643        // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
644        // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
645        // runtime might not allow coercing at that call site.
646        final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
647        final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
648        // Must have exactly two arguments: receiver and name
649        assertParameterCount(callSiteDescriptor, 2);
650
651        // What's below is basically:
652        //   foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
653        // only with a bunch of method signature adjustments. Basically, retrieve method getter
654        // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
655        // or delegate to next component's invocation.
656
657        final LinkerServices linkerServices = req.linkerServices;
658        final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
659                AnnotatedDynamicMethod.class));
660        final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
661                GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices);
662        final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
663                callSiteBoundMethodGetter);
664        // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
665        final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
666                MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
667        // Since it's in the target of a fold, drop the unnecessary second argument
668        // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
669        final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
670                type.parameterType(1));
671        final GuardedInvocationComponent nextComponent = getNextComponent(req);
672
673        final MethodHandle fallbackFolded;
674        if(nextComponent == null) {
675            // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
676            fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
677                    type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
678        } else {
679            // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
680            // drop the extra argument resulting from fold and to change its return type to Object.
681            final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
682            final MethodType nextType = nextInvocation.type();
683            fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
684                    nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
685        }
686
687        // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
688        final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
689                    IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
690        if(nextComponent == null) {
691            return getClassGuardedInvocationComponent(compositeGetter, type);
692        }
693        return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
694    }
695
696    private GuardedInvocationComponent getNamedPropertyGetter(final ComponentLinkRequest req) throws Exception {
697        final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
698        // Must have exactly one argument: receiver
699        assertParameterCount(callSiteDescriptor, 1);
700        // Fixed name
701        final AnnotatedDynamicMethod annGetter = propertyGetters.get(req.name.toString());
702        if(annGetter == null) {
703            // We have no such property, always delegate to the next component operation
704            return getNextComponent(req);
705        }
706        final MethodHandle getter = annGetter.getInvocation(req);
707        // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
708        // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
709        // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
710        // we're linking against a field getter, don't make the assumption.
711        // NOTE: No delegation to the next component operation if we have a property with this name, even if its
712        // value is null.
713        final ValidationType validationType = annGetter.validationType;
714        // TODO: we aren't using the type that declares the most generic getter here!
715        return new GuardedInvocationComponent(getter, getGuard(validationType,
716                callSiteDescriptor.getMethodType()), clazz, validationType);
717    }
718
719    private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
720        switch(validationType) {
721            case EXACT_CLASS: {
722                return getClassGuard(methodType);
723            }
724            case INSTANCE_OF: {
725                return getAssignableGuard(methodType);
726            }
727            case IS_ARRAY: {
728                return Guards.isArray(0, methodType);
729            }
730            case NONE: {
731                return null;
732            }
733            default: {
734                throw new AssertionError();
735            }
736        }
737    }
738
739    private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
740            MethodType.methodType(boolean.class, Object.class));
741    private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
742
743    private GuardedInvocationComponent getMethodGetter(final ComponentLinkRequest req) throws Exception {
744        if (req.name == null) {
745            return getUnnamedMethodGetter(req);
746        }
747        return getNamedMethodGetter(req);
748    }
749
750    private static MethodType getMethodGetterType(final ComponentLinkRequest req) {
751        // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
752        // be visible outside of this linker, declare it to return Object.
753        return req.getDescriptor().getMethodType().changeReturnType(Object.class);
754    }
755
756    private GuardedInvocationComponent getUnnamedMethodGetter(final ComponentLinkRequest req) throws Exception {
757        // Must have exactly two arguments: receiver and name
758        assertParameterCount(req.getDescriptor(), 2);
759        final GuardedInvocationComponent nextComponent = getNextComponent(req);
760        final LinkerServices linkerServices = req.linkerServices;
761        final MethodType type = getMethodGetterType(req);
762        if(nextComponent == null) {
763            // No next component operation; just return a component for this operation.
764            return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
765        }
766
767        // What's below is basically:
768        // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
769        // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
770        // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
771
772        final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
773        // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
774        final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
775                OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
776        final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
777        // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
778        // return type.
779        assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
780        // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
781        final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
782                Object.class);
783        // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
784        // Note that nextCombinedInvocation needs to have its return type changed to Object
785        final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
786                IS_DYNAMIC_METHOD, returnMethodHandle,
787                nextCombinedInvocation.asType(nextCombinedInvocation.type().changeReturnType(Object.class))),
788                typedGetter);
789
790        return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
791    }
792
793    private GuardedInvocationComponent getNamedMethodGetter(final ComponentLinkRequest req)
794            throws Exception {
795        // Must have exactly one argument: receiver
796        assertParameterCount(req.getDescriptor(), 1);
797        final DynamicMethod method = getDynamicMethod(req.name.toString());
798        if(method == null) {
799            // We have no such method, always delegate to the next component
800            return getNextComponent(req);
801        }
802        // No delegation to the next namespace; if we have a method with that name, we'll always return it at
803        // this point.
804        final MethodType type = getMethodGetterType(req);
805        return getClassGuardedInvocationComponent(req.linkerServices.asType(MethodHandles.dropArguments(
806                MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
807    }
808
809    static class MethodPair {
810        final MethodHandle method1;
811        final MethodHandle method2;
812
813        MethodPair(final MethodHandle method1, final MethodHandle method2) {
814            this.method1 = method1;
815            this.method2 = method2;
816        }
817
818        MethodHandle guardWithTest(final MethodHandle test) {
819            return MethodHandles.guardWithTest(test, method1, method2);
820        }
821    }
822
823    static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {
824        final MethodType type1 = m1.type();
825        final MethodType type2 = m2.type();
826        final Class<?> commonRetType = InternalTypeUtilities.getCommonLosslessConversionType(type1.returnType(),
827                type2.returnType());
828        return new MethodPair(
829                m1.asType(type1.changeReturnType(commonRetType)),
830                m2.asType(type2.changeReturnType(commonRetType)));
831    }
832
833    private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
834        if(descriptor.getMethodType().parameterCount() != paramCount) {
835            throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters.");
836        }
837    }
838
839    private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
840            "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
841    private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
842
843    /**
844     * @param id the property ID
845     * @return the method handle for retrieving the property, or null if the property does not exist
846     */
847    @SuppressWarnings("unused")
848    private Object getPropertyGetterHandle(final Object id) {
849        return propertyGetters.get(String.valueOf(id));
850    }
851
852    // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
853    // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
854    // a typical property setter with variable name signature (target, name, value).
855    private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
856            privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
857                    LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
858    // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
859    private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
860
861    @SuppressWarnings("unused")
862    private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,
863            final Object id) {
864        return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
865    }
866
867    private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
868            "getDynamicMethod", Object.class, Object.class), 1, Object.class);
869    private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
870
871    @SuppressWarnings("unused")
872    // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
873    // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
874    // GET:METHOD linking).
875    private Object getDynamicMethod(final Object name) {
876        return getDynamicMethod(String.valueOf(name), methods);
877    }
878
879    /**
880     * Returns a dynamic method of the specified name.
881     *
882     * @param name name of the method
883     * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
884     * method with the specified name does not exist.
885     */
886    DynamicMethod getDynamicMethod(final String name) {
887        return getDynamicMethod(name, methods);
888    }
889
890    /**
891     * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
892     * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
893     * creating more stable call sites.
894     * @param getter the getter
895     * @return getter with same name, declared on the most generic superclass/interface of the declaring class
896     */
897    private static Method getMostGenericGetter(final Method getter) {
898        return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
899    }
900
901    private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) {
902        if(declaringClass == null) {
903            return null;
904        }
905        // Prefer interfaces
906        for(final Class<?> itf: declaringClass.getInterfaces()) {
907            final Method itfGetter = getMostGenericGetter(name, returnType, itf);
908            if(itfGetter != null) {
909                return itfGetter;
910            }
911        }
912        final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
913        if(superGetter != null) {
914            return superGetter;
915        }
916        if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
917            try {
918                return declaringClass.getMethod(name);
919            } catch(final NoSuchMethodException e) {
920                // Intentionally ignored, meant to fall through
921            }
922        }
923        return null;
924    }
925
926    private static final class AnnotatedDynamicMethod {
927        private final SingleDynamicMethod method;
928        /*private*/ final ValidationType validationType;
929
930        AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {
931            this.method = method;
932            this.validationType = validationType;
933        }
934
935        MethodHandle getInvocation(final ComponentLinkRequest req) {
936            return method.getInvocation(req.getDescriptor(), req.linkerServices);
937        }
938
939        @SuppressWarnings("unused")
940        MethodHandle getTarget(final CallSiteDescriptor desc, final LinkerServices linkerServices) {
941            final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(desc));
942            assert inv != null;
943            return inv;
944        }
945    }
946}
947