blob: 75c5065d52524121ae3d7ce63ae89c33b1f4ecc7 [file] [log] [blame]
Attila Szegedi82f8cef2013-02-14 13:22:26 +01001/*
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
Attila Szegedi505e5092015-11-24 10:19:34 +010084package jdk.dynalink.beans;
Attila Szegedi82f8cef2013-02-14 13:22:26 +010085
Attila Szegedi82f8cef2013-02-14 13:22:26 +010086import java.lang.invoke.MethodHandle;
87import java.lang.invoke.MethodHandles;
88import java.lang.invoke.MethodType;
Attila Szegedi92bcfea2013-07-03 12:39:28 +020089import java.lang.reflect.AccessibleObject;
90import java.lang.reflect.Constructor;
Attila Szegedi82f8cef2013-02-14 13:22:26 +010091import java.lang.reflect.Field;
Attila Szegedi92bcfea2013-07-03 12:39:28 +020092import java.lang.reflect.Member;
Attila Szegedi82f8cef2013-02-14 13:22:26 +010093import java.lang.reflect.Method;
94import java.lang.reflect.Modifier;
Attila Szegedife130342015-10-21 19:33:58 +020095import java.util.Arrays;
Attila Szegedie11a9b12013-07-15 12:33:48 +020096import java.util.Collections;
Attila Szegedi82f8cef2013-02-14 13:22:26 +010097import java.util.HashMap;
98import java.util.List;
99import java.util.Map;
Attila Szegedi534ff3d2015-11-11 14:54:09 +0100100import java.util.Set;
Attila Szegedi505e5092015-11-24 10:19:34 +0100101import jdk.dynalink.CallSiteDescriptor;
102import jdk.dynalink.CompositeOperation;
103import jdk.dynalink.NamedOperation;
104import jdk.dynalink.Operation;
105import jdk.dynalink.StandardOperation;
106import jdk.dynalink.beans.GuardedInvocationComponent.ValidationType;
107import jdk.dynalink.internal.InternalTypeUtilities;
108import jdk.dynalink.linker.GuardedInvocation;
109import jdk.dynalink.linker.GuardingDynamicLinker;
110import jdk.dynalink.linker.LinkRequest;
111import jdk.dynalink.linker.LinkerServices;
112import jdk.dynalink.linker.support.Guards;
113import jdk.dynalink.linker.support.Lookup;
Chris Hegarty9ec15902016-04-15 16:19:15 +0100114import jdk.internal.reflect.CallerSensitive;
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100115
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100116/**
117 * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
118 * exposure and method calls for both static and instance facets of a class.
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100119 */
120abstract class AbstractJavaLinker implements GuardingDynamicLinker {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200121
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100122 final Class<?> clazz;
123 private final MethodHandle classGuard;
124 private final MethodHandle assignableGuard;
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200125 private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100126 private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
127 private final Map<String, DynamicMethod> methods = new HashMap<>();
128
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200129 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100130 this(clazz, classGuard, classGuard);
131 }
132
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200133 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100134 this.clazz = clazz;
135 this.classGuard = classGuard;
136 this.assignableGuard = assignableGuard;
137
138 final FacetIntrospector introspector = createFacetIntrospector();
Attila Szegediccce3fa2013-02-18 16:00:15 +0100139 // Add methods and properties
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200140 for(final Method method: introspector.getMethods()) {
Attila Szegediccce3fa2013-02-18 16:00:15 +0100141 final String name = method.getName();
Attila Szegediccce3fa2013-02-18 16:00:15 +0100142 // Add method
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200143 addMember(name, method, methods);
Attila Szegediccce3fa2013-02-18 16:00:15 +0100144 // Add the method as a property getter and/or setter
145 if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
146 // Property getter
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200147 setPropertyGetter(method, 3);
Attila Szegediccce3fa2013-02-18 16:00:15 +0100148 } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
149 method.getReturnType() == boolean.class) {
150 // Boolean property getter
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200151 setPropertyGetter(method, 2);
Attila Szegediccce3fa2013-02-18 16:00:15 +0100152 } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
153 // Property setter
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200154 addMember(decapitalize(name.substring(3)), method, propertySetters);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100155 }
Attila Szegediccce3fa2013-02-18 16:00:15 +0100156 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100157
Attila Szegediccce3fa2013-02-18 16:00:15 +0100158 // Add field getter/setters as property getters/setters.
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200159 for(final Field field: introspector.getFields()) {
Attila Szegediccce3fa2013-02-18 16:00:15 +0100160 final String name = field.getName();
161 // Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
162 if(!propertyGetters.containsKey(name)) {
163 setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100164 }
Attila Szegediccce3fa2013-02-18 16:00:15 +0100165 if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200166 addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),
167 propertySetters);
Attila Szegediccce3fa2013-02-18 16:00:15 +0100168 }
169 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100170
Attila Szegediccce3fa2013-02-18 16:00:15 +0100171 // Add inner classes, but only those for which we don't hide a property with it
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200172 for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
Attila Szegediccce3fa2013-02-18 16:00:15 +0100173 final String name = innerClassSpec.getKey();
174 if(!propertyGetters.containsKey(name)) {
175 setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100176 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100177 }
178 }
179
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200180 private static String decapitalize(final String str) {
Attila Szegedia1ade222013-02-27 15:20:26 +0100181 assert str != null;
182 if(str.isEmpty()) {
183 return str;
184 }
185
186 final char c0 = str.charAt(0);
187 if(Character.isLowerCase(c0)) {
188 return str;
189 }
190
191 // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize
192 if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {
193 return str;
194 }
195
196 final char c[] = str.toCharArray();
197 c[0] = Character.toLowerCase(c0);
198 return new String(c);
199 }
200
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100201 abstract FacetIntrospector createFacetIntrospector();
202
Attila Szegedi534ff3d2015-11-11 14:54:09 +0100203 Set<String> getReadablePropertyNames() {
Attila Szegedie11a9b12013-07-15 12:33:48 +0200204 return getUnmodifiableKeys(propertyGetters);
205 }
206
Attila Szegedi534ff3d2015-11-11 14:54:09 +0100207 Set<String> getWritablePropertyNames() {
Attila Szegedie11a9b12013-07-15 12:33:48 +0200208 return getUnmodifiableKeys(propertySetters);
209 }
210
Attila Szegedi534ff3d2015-11-11 14:54:09 +0100211 Set<String> getMethodNames() {
Attila Szegedie11a9b12013-07-15 12:33:48 +0200212 return getUnmodifiableKeys(methods);
213 }
214
Attila Szegedi534ff3d2015-11-11 14:54:09 +0100215 private static Set<String> getUnmodifiableKeys(final Map<String, ?> m) {
216 return Collections.unmodifiableSet(m.keySet());
Attila Szegedie11a9b12013-07-15 12:33:48 +0200217 }
218
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200219 /**
220 * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
221 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
222 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
223 * instead.
224 * @param name name of the property
225 * @param handle the method handle that implements the property getter
226 * @param validationType the validation type for the property
227 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200228 private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200229 propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100230 }
231
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200232 /**
233 * Sets the specified reflective method to be the property getter for the specified property.
234 * @param getter the getter method
235 * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
236 * names starting with "is".
237 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200238 private void setPropertyGetter(final Method getter, final int prefixLen) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200239 setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
240 getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
241 }
242
243 /**
244 * Sets the specified method handle to be the property getter for the specified property. Note that you can only
245 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
246 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
247 * instead.
248 * @param name name of the property
249 * @param handle the method handle that implements the property getter
250 * @param validationType the validation type for the property
251 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200252 void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200253 setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
254 }
255
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200256 private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200257 addMember(name, createDynamicMethod(ao), methodMap);
258 }
259
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200260 private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100261 final DynamicMethod existingMethod = methodMap.get(name);
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200262 final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100263 if(newMethod != existingMethod) {
264 methodMap.put(name, newMethod);
265 }
266 }
267
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200268 /**
269 * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The
270 * methods should represent all overloads of the same name (or all constructors of the class).
271 * @param members the reflective members
272 * @param clazz the class declaring the reflective members
273 * @param name the common name of the reflective members.
274 * @return a dynamic method representing all the specified reflective members.
275 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200276 static DynamicMethod createDynamicMethod(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100277 DynamicMethod dynMethod = null;
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200278 for(final AccessibleObject method: members) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200279 dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100280 }
281 return dynMethod;
282 }
283
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200284 /**
285 * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will
286 * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive
287 * dynamic method when needed.
288 * @param m the reflective member
289 * @return the single dynamic method representing the reflective member
290 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200291 private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
Attila Szegedieba30092015-10-19 08:23:03 +0200292 if (m.isAnnotationPresent(CallerSensitive.class)) {
Attila Szegedi10ebc442014-11-03 07:29:46 +0100293 // Method has @CallerSensitive annotation
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200294 return new CallerSensitiveDynamicMethod(m);
295 }
Attila Szegedi10ebc442014-11-03 07:29:46 +0100296 // Method has no @CallerSensitive annotation
297 final MethodHandle mh;
298 try {
299 mh = unreflectSafely(m);
300 } catch (final IllegalAccessError e) {
301 // java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
302 // marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
303 return new CallerSensitiveDynamicMethod(m);
304 }
305 // Proceed with non-caller sensitive
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200306 final Member member = (Member)m;
Attila Szegedi10ebc442014-11-03 07:29:46 +0100307 return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200308 }
309
310 /**
311 * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be
312 * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were
313 * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege
314 * unreflector as its caller, and thus completely useless.
315 * @param m the method or constructor
316 * @return the method handle
317 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200318 private static MethodHandle unreflectSafely(final AccessibleObject m) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200319 if(m instanceof Method) {
320 final Method reflMethod = (Method)m;
Attila Szegedi91e5fbd2013-07-24 11:13:24 +0200321 final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200322 if(Modifier.isStatic(reflMethod.getModifiers())) {
323 return StaticClassIntrospector.editStaticMethodHandle(handle);
324 }
325 return handle;
326 }
Attila Szegedi91e5fbd2013-07-24 11:13:24 +0200327 return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200328 }
329
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200330 private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100331 if(existing == null) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200332 return method;
333 } else if(existing.contains(method)) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100334 return existing;
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200335 } else if(existing instanceof SingleDynamicMethod) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100336 final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200337 odm.addMethod(((SingleDynamicMethod)existing));
338 odm.addMethod(method);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100339 return odm;
340 } else if(existing instanceof OverloadedDynamicMethod) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200341 ((OverloadedDynamicMethod)existing).addMethod(method);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100342 return existing;
343 }
344 throw new AssertionError();
345 }
346
347 @Override
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200348 public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100349 throws Exception {
Attila Szegedi57a2ec82015-10-19 08:39:06 +0200350 final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor();
Attila Szegedife130342015-10-21 19:33:58 +0200351
Attila Szegedidf1606c2016-01-14 13:24:03 +0100352 final MissingMemberHandlerFactory missingMemberHandlerFactory;
353 final LinkerServices directLinkerServices;
354 if (linkerServices instanceof LinkerServicesWithMissingMemberHandlerFactory) {
355 final LinkerServicesWithMissingMemberHandlerFactory lswmmhf = ((LinkerServicesWithMissingMemberHandlerFactory)linkerServices);
356 missingMemberHandlerFactory = lswmmhf.missingMemberHandlerFactory;
357 directLinkerServices = lswmmhf.linkerServices;
358 } else {
359 missingMemberHandlerFactory = null;
360 directLinkerServices = linkerServices;
361 }
362
Attila Szegedife130342015-10-21 19:33:58 +0200363 // Handle NamedOperation(CALL_METHOD, name) separately
364 final Operation operation = callSiteDescriptor.getOperation();
365 if (operation instanceof NamedOperation) {
366 final NamedOperation namedOperation = (NamedOperation)operation;
367 if (namedOperation.getBaseOperation() == StandardOperation.CALL_METHOD) {
Attila Szegedidf1606c2016-01-14 13:24:03 +0100368 final GuardedInvocation inv =
369 createGuardedDynamicMethodInvocation(callSiteDescriptor,
370 directLinkerServices, namedOperation.getName().toString(), methods);
371 if (inv == null) {
372 return createNoSuchMemberHandler(missingMemberHandlerFactory,
373 request, directLinkerServices).getGuardedInvocation();
374 }
375 return inv;
Attila Szegedife130342015-10-21 19:33:58 +0200376 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100377 }
Attila Szegedife130342015-10-21 19:33:58 +0200378
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100379 final GuardedInvocationComponent gic = getGuardedInvocationComponent(
Attila Szegedidf1606c2016-01-14 13:24:03 +0100380 new ComponentLinkRequest(request, directLinkerServices,
381 missingMemberHandlerFactory));
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100382 return gic != null ? gic.getGuardedInvocation() : null;
383 }
Attila Szegedife130342015-10-21 19:33:58 +0200384
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100385 static final class ComponentLinkRequest {
386 final LinkRequest linkRequest;
387 final LinkerServices linkerServices;
Attila Szegedidf1606c2016-01-14 13:24:03 +0100388 final MissingMemberHandlerFactory missingMemberHandlerFactory;
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100389 final List<Operation> operations;
390 final Object name;
391
392 ComponentLinkRequest(final LinkRequest linkRequest,
Attila Szegedidf1606c2016-01-14 13:24:03 +0100393 final LinkerServices linkerServices,
394 final MissingMemberHandlerFactory missingMemberHandlerFactory) {
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100395 this.linkRequest = linkRequest;
396 this.linkerServices = linkerServices;
Attila Szegedidf1606c2016-01-14 13:24:03 +0100397 this.missingMemberHandlerFactory = missingMemberHandlerFactory;
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100398 final Operation operation = linkRequest.getCallSiteDescriptor().getOperation();
399 this.operations = Arrays.asList(
400 CompositeOperation.getOperations(
401 NamedOperation.getBaseOperation(operation)));
402 this.name = NamedOperation.getName(operation);
403 }
404
405 private ComponentLinkRequest(final LinkRequest linkRequest,
406 final LinkerServices linkerServices,
Attila Szegedidf1606c2016-01-14 13:24:03 +0100407 final MissingMemberHandlerFactory missingMemberHandlerFactory,
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100408 final List<Operation> operations, final Object name) {
409 this.linkRequest = linkRequest;
410 this.linkerServices = linkerServices;
Attila Szegedidf1606c2016-01-14 13:24:03 +0100411 this.missingMemberHandlerFactory = missingMemberHandlerFactory;
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100412 this.operations = operations;
413 this.name = name;
414 }
415
416 CallSiteDescriptor getDescriptor() {
417 return linkRequest.getCallSiteDescriptor();
418 }
419
420 ComponentLinkRequest popOperations() {
421 return new ComponentLinkRequest(linkRequest, linkerServices,
Attila Szegedidf1606c2016-01-14 13:24:03 +0100422 missingMemberHandlerFactory,
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100423 operations.subList(1, operations.size()), name);
424 }
425 }
426
427 protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req)
428 throws Exception {
429 final Operation op = req.operations.get(0);
430 if (op instanceof StandardOperation) {
431 switch((StandardOperation)op) {
432 case GET_PROPERTY: return getPropertyGetter(req.popOperations());
433 case SET_PROPERTY: return getPropertySetter(req.popOperations());
434 case GET_METHOD: return getMethodGetter(req.popOperations());
435 default:
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100436 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100437 }
438 return null;
439 }
440
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100441 GuardedInvocationComponent getNextComponent(final ComponentLinkRequest req) throws Exception {
442 if (req.operations.isEmpty()) {
Attila Szegedidf1606c2016-01-14 13:24:03 +0100443 return createNoSuchMemberHandler(req.missingMemberHandlerFactory,
444 req.linkRequest, req.linkerServices);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100445 }
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100446 final GuardedInvocationComponent gic = getGuardedInvocationComponent(req);
447 if (gic != null) {
448 return gic;
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100449 }
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100450 return getNextComponent(req.popOperations());
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100451 }
452
Attila Szegedidf1606c2016-01-14 13:24:03 +0100453 private GuardedInvocationComponent createNoSuchMemberHandler(
454 final MissingMemberHandlerFactory missingMemberHandlerFactory,
455 final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
456 if (missingMemberHandlerFactory == null) {
457 return null;
458 }
459 final MethodHandle handler = missingMemberHandlerFactory.createMissingMemberHandler(linkRequest, linkerServices);
460 if (handler == null) {
461 return null;
462 }
463 final MethodType type = linkRequest.getCallSiteDescriptor().getMethodType();
464 // The returned handler is allowed to differ in return type.
465 assert handler.type().changeReturnType(type.returnType()).equals(type);
466 return getClassGuardedInvocationComponent(handler, type);
467 }
468
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200469 static final <T> List<T> pop(final List<T> l) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100470 return l.subList(1, l.size());
471 }
472
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200473 MethodHandle getClassGuard(final CallSiteDescriptor desc) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100474 return getClassGuard(desc.getMethodType());
475 }
476
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200477 MethodHandle getClassGuard(final MethodType type) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100478 return Guards.asType(classGuard, type);
479 }
480
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200481 GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100482 return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
483 }
484
Attila Szegedi534ff3d2015-11-11 14:54:09 +0100485 abstract SingleDynamicMethod getConstructorMethod(final String signature);
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530486
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200487 private MethodHandle getAssignableGuard(final MethodType type) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100488 return Guards.asType(assignableGuard, type);
489 }
490
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200491 private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
492 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200493 final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
494 return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100495 }
496
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530497 private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200498 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100499 final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200500 return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100501 }
502
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530503 private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100504 final DynamicMethod dynaMethod = methodMap.get(methodName);
505 return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
506 }
507
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530508 private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName,
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200509 final Map<String, DynamicMethod> methodsMap) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100510 // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
511 // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
512 // resolution works correctly in almost every situation. However, in presence of many language-specific
513 // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200514 // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100515 // for performance reasons.
516
517 // Is the method name lexically of the form "name(types)"?
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530518 final int lastChar = fullName.length() - 1;
519 if(fullName.charAt(lastChar) != ')') {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100520 return null;
521 }
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530522 final int openBrace = fullName.indexOf('(');
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100523 if(openBrace == -1) {
524 return null;
525 }
526
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530527 final String name = fullName.substring(0, openBrace);
528 final String signature = fullName.substring(openBrace + 1, lastChar);
529
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100530 // Find an existing method for the "name" part
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530531 final DynamicMethod simpleNamedMethod = methodsMap.get(name);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100532 if(simpleNamedMethod == null) {
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530533 // explicit signature constructor access
534 // Java.type("java.awt.Color")["(int,int,int)"]
535 // will get Color(int,int,int) constructor of Color class.
536 if (name.isEmpty()) {
537 return getConstructorMethod(signature);
538 }
539
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100540 return null;
541 }
542
543 // Try to get a narrowed dynamic method for the explicit parameter types.
Athijegannathan Sundararajan14eb01c2014-07-03 23:03:37 +0530544 return simpleNamedMethod.getMethodForExactParamTypes(signature);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100545 }
546
547 private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
548 boolean.class, MethodHandle.class));
549 private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
550 MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
551
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100552 private GuardedInvocationComponent getPropertySetter(final ComponentLinkRequest req) throws Exception {
553 if (req.name == null) {
554 return getUnnamedPropertySetter(req);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100555 }
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100556 return getNamedPropertySetter(req);
Attila Szegedife130342015-10-21 19:33:58 +0200557 }
558
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100559 private GuardedInvocationComponent getUnnamedPropertySetter(final ComponentLinkRequest req) throws Exception {
560 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
Attila Szegedife130342015-10-21 19:33:58 +0200561 // Must have three arguments: target object, property name, and property value.
562 assertParameterCount(callSiteDescriptor, 3);
563
564 // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
565 // valid for us to convert return values proactively. Also, since we don't know what setters will be
566 // invoked, we'll conservatively presume Object return type. The one exception is void return.
567 final MethodType origType = callSiteDescriptor.getMethodType();
568 final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100569 final LinkerServices linkerServices = req.linkerServices;
Attila Szegedife130342015-10-21 19:33:58 +0200570
571 // What's below is basically:
572 // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
573 // get_setter_handle(type, linkerServices))
574 // only with a bunch of method signature adjustments. Basically, retrieve method setter
575 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
576 // component's invocation.
577
578 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
579 // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
580 // Object return type).
581 final MethodType setterType = type.dropParameterTypes(1, 2);
582 // Bind property setter handle to the expected setter type and linker services. Type is
583 // MethodHandle(Object, String, Object)
584 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
585 callSiteDescriptor.changeMethodType(setterType), linkerServices);
586
587 // Cast getter to MethodHandle(O, N, V)
588 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
589 MethodHandle.class));
590
591 // Handle to invoke the setter R(MethodHandle, O, V)
592 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
593 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
594 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
595 1));
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100596 final GuardedInvocationComponent nextComponent = getNextComponent(req);
Attila Szegedife130342015-10-21 19:33:58 +0200597
598 final MethodHandle fallbackFolded;
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100599 if (nextComponent == null) {
Attila Szegedife130342015-10-21 19:33:58 +0200600 // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
601 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
602 type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
603 } else {
604 // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
605 // extra argument resulting from fold
606 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
607 0, MethodHandle.class);
608 }
609
610 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
611 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
612 IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
613 if(nextComponent == null) {
614 return getClassGuardedInvocationComponent(compositeSetter, type);
615 }
616 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
617 }
618
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100619 private GuardedInvocationComponent getNamedPropertySetter(final ComponentLinkRequest req) throws Exception {
620 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
Attila Szegedife130342015-10-21 19:33:58 +0200621 // Must have two arguments: target object and property value
622 assertParameterCount(callSiteDescriptor, 2);
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100623 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, req.linkerServices,
624 req.name.toString(), propertySetters);
Attila Szegedife130342015-10-21 19:33:58 +0200625 // If we have a property setter with this name, this composite operation will always stop here
626 if(gi != null) {
627 return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
628 }
629 // If we don't have a property setter with this name, always fall back to the next operation in the
630 // composite (if any)
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100631 return getNextComponent(req);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100632 }
633
634 private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
635
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200636 private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
637 boolean.class, AnnotatedDynamicMethod.class));
638 private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
639 MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
640 private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
Attila Szegedi8f8c3ea2015-10-19 08:30:03 +0200641 "getTarget", MethodType.methodType(MethodHandle.class, CallSiteDescriptor.class, LinkerServices.class));
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200642 private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100643
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100644 private GuardedInvocationComponent getPropertyGetter(final ComponentLinkRequest req) throws Exception {
645 if (req.name == null) {
646 return getUnnamedPropertyGetter(req);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100647 }
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100648 return getNamedPropertyGetter(req);
Attila Szegedife130342015-10-21 19:33:58 +0200649 }
650
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100651 private GuardedInvocationComponent getUnnamedPropertyGetter(final ComponentLinkRequest req) throws Exception {
Attila Szegedife130342015-10-21 19:33:58 +0200652 // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
653 // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
654 // runtime might not allow coercing at that call site.
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100655 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
Attila Szegedife130342015-10-21 19:33:58 +0200656 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
657 // Must have exactly two arguments: receiver and name
658 assertParameterCount(callSiteDescriptor, 2);
659
660 // What's below is basically:
661 // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
662 // only with a bunch of method signature adjustments. Basically, retrieve method getter
663 // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
664 // or delegate to next component's invocation.
665
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100666 final LinkerServices linkerServices = req.linkerServices;
Attila Szegedife130342015-10-21 19:33:58 +0200667 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
668 AnnotatedDynamicMethod.class));
669 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
670 GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices);
671 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
672 callSiteBoundMethodGetter);
673 // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
674 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
675 MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
676 // Since it's in the target of a fold, drop the unnecessary second argument
677 // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
678 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
679 type.parameterType(1));
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100680 final GuardedInvocationComponent nextComponent = getNextComponent(req);
Attila Szegedife130342015-10-21 19:33:58 +0200681
682 final MethodHandle fallbackFolded;
683 if(nextComponent == null) {
684 // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
685 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
686 type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
687 } else {
688 // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
689 // drop the extra argument resulting from fold and to change its return type to Object.
690 final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
691 final MethodType nextType = nextInvocation.type();
692 fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
693 nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
694 }
695
696 // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
697 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
698 IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
699 if(nextComponent == null) {
700 return getClassGuardedInvocationComponent(compositeGetter, type);
701 }
702 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
703 }
704
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100705 private GuardedInvocationComponent getNamedPropertyGetter(final ComponentLinkRequest req) throws Exception {
706 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
Attila Szegedife130342015-10-21 19:33:58 +0200707 // Must have exactly one argument: receiver
708 assertParameterCount(callSiteDescriptor, 1);
709 // Fixed name
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100710 final AnnotatedDynamicMethod annGetter = propertyGetters.get(req.name.toString());
Attila Szegedife130342015-10-21 19:33:58 +0200711 if(annGetter == null) {
712 // We have no such property, always delegate to the next component operation
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100713 return getNextComponent(req);
Attila Szegedife130342015-10-21 19:33:58 +0200714 }
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100715 final MethodHandle getter = annGetter.getInvocation(req);
Attila Szegedife130342015-10-21 19:33:58 +0200716 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
717 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
718 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
719 // we're linking against a field getter, don't make the assumption.
720 // NOTE: No delegation to the next component operation if we have a property with this name, even if its
721 // value is null.
722 final ValidationType validationType = annGetter.validationType;
723 // TODO: we aren't using the type that declares the most generic getter here!
724 return new GuardedInvocationComponent(getter, getGuard(validationType,
725 callSiteDescriptor.getMethodType()), clazz, validationType);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100726 }
727
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200728 private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100729 switch(validationType) {
730 case EXACT_CLASS: {
731 return getClassGuard(methodType);
732 }
733 case INSTANCE_OF: {
734 return getAssignableGuard(methodType);
735 }
736 case IS_ARRAY: {
737 return Guards.isArray(0, methodType);
738 }
739 case NONE: {
740 return null;
741 }
Attila Szegediccce3fa2013-02-18 16:00:15 +0100742 default: {
743 throw new AssertionError();
744 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100745 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100746 }
747
Attila Szegedie9e7dd22014-02-26 13:17:57 +0100748 private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
749 MethodType.methodType(boolean.class, Object.class));
750 private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100751
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100752 private GuardedInvocationComponent getMethodGetter(final ComponentLinkRequest req) throws Exception {
753 if (req.name == null) {
754 return getUnnamedMethodGetter(req);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100755 }
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100756 return getNamedMethodGetter(req);
Attila Szegedife130342015-10-21 19:33:58 +0200757 }
758
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100759 private static MethodType getMethodGetterType(final ComponentLinkRequest req) {
760 // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
761 // be visible outside of this linker, declare it to return Object.
762 return req.getDescriptor().getMethodType().changeReturnType(Object.class);
763 }
764
765 private GuardedInvocationComponent getUnnamedMethodGetter(final ComponentLinkRequest req) throws Exception {
Attila Szegedife130342015-10-21 19:33:58 +0200766 // Must have exactly two arguments: receiver and name
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100767 assertParameterCount(req.getDescriptor(), 2);
768 final GuardedInvocationComponent nextComponent = getNextComponent(req);
769 final LinkerServices linkerServices = req.linkerServices;
770 final MethodType type = getMethodGetterType(req);
771 if(nextComponent == null) {
772 // No next component operation; just return a component for this operation.
Attila Szegedife130342015-10-21 19:33:58 +0200773 return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
774 }
775
776 // What's below is basically:
777 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
778 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
779 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
780
781 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
782 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
783 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
784 OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
785 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
786 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
787 // return type.
788 assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
789 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
790 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
791 Object.class);
792 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100793 // Note that nextCombinedInvocation needs to have its return type changed to Object
Attila Szegedife130342015-10-21 19:33:58 +0200794 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100795 IS_DYNAMIC_METHOD, returnMethodHandle,
796 nextCombinedInvocation.asType(nextCombinedInvocation.type().changeReturnType(Object.class))),
797 typedGetter);
Attila Szegedife130342015-10-21 19:33:58 +0200798
799 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
800 }
801
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100802 private GuardedInvocationComponent getNamedMethodGetter(final ComponentLinkRequest req)
Attila Szegedife130342015-10-21 19:33:58 +0200803 throws Exception {
804 // Must have exactly one argument: receiver
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100805 assertParameterCount(req.getDescriptor(), 1);
806 final DynamicMethod method = getDynamicMethod(req.name.toString());
Attila Szegedife130342015-10-21 19:33:58 +0200807 if(method == null) {
808 // We have no such method, always delegate to the next component
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100809 return getNextComponent(req);
Attila Szegedife130342015-10-21 19:33:58 +0200810 }
811 // No delegation to the next component of the composite operation; if we have a method with that name,
812 // we'll always return it at this point.
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100813 final MethodType type = getMethodGetterType(req);
814 return getClassGuardedInvocationComponent(req.linkerServices.asType(MethodHandles.dropArguments(
Attila Szegedife130342015-10-21 19:33:58 +0200815 MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100816 }
817
Attila Szegedie9e7dd22014-02-26 13:17:57 +0100818 static class MethodPair {
819 final MethodHandle method1;
820 final MethodHandle method2;
821
822 MethodPair(final MethodHandle method1, final MethodHandle method2) {
823 this.method1 = method1;
824 this.method2 = method2;
825 }
826
827 MethodHandle guardWithTest(final MethodHandle test) {
828 return MethodHandles.guardWithTest(test, method1, method2);
829 }
830 }
831
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200832 static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {
Attila Szegedie9e7dd22014-02-26 13:17:57 +0100833 final MethodType type1 = m1.type();
834 final MethodType type2 = m2.type();
Attila Szegedi4b535d32015-10-20 23:33:39 +0200835 final Class<?> commonRetType = InternalTypeUtilities.getCommonLosslessConversionType(type1.returnType(),
Attila Szegedie9e7dd22014-02-26 13:17:57 +0100836 type2.returnType());
837 return new MethodPair(
838 m1.asType(type1.changeReturnType(commonRetType)),
839 m2.asType(type2.changeReturnType(commonRetType)));
840 }
841
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200842 private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100843 if(descriptor.getMethodType().parameterCount() != paramCount) {
Attila Szegedife130342015-10-21 19:33:58 +0200844 throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters.");
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100845 }
846 }
847
848 private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
849 "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
850 private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
851
852 /**
853 * @param id the property ID
854 * @return the method handle for retrieving the property, or null if the property does not exist
855 */
856 @SuppressWarnings("unused")
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200857 private Object getPropertyGetterHandle(final Object id) {
Athijegannathan Sundararajan9f0dcb12015-12-24 16:52:06 +0530858 return propertyGetters.get(String.valueOf(id));
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100859 }
860
861 // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
862 // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
863 // a typical property setter with variable name signature (target, name, value).
864 private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200865 privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100866 LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
867 // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
868 private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
869
870 @SuppressWarnings("unused")
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200871 private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,
872 final Object id) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200873 return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100874 }
875
876 private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
Attila Szegedie9e7dd22014-02-26 13:17:57 +0100877 "getDynamicMethod", Object.class, Object.class), 1, Object.class);
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100878 private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
879
880 @SuppressWarnings("unused")
Attila Szegedie9e7dd22014-02-26 13:17:57 +0100881 // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
882 // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
Attila Szegedife130342015-10-21 19:33:58 +0200883 // GET_METHOD linking).
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200884 private Object getDynamicMethod(final Object name) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100885 return getDynamicMethod(String.valueOf(name), methods);
886 }
887
888 /**
889 * Returns a dynamic method of the specified name.
890 *
891 * @param name name of the method
892 * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
893 * method with the specified name does not exist.
894 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200895 DynamicMethod getDynamicMethod(final String name) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100896 return getDynamicMethod(name, methods);
897 }
898
899 /**
900 * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
901 * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
902 * creating more stable call sites.
903 * @param getter the getter
904 * @return getter with same name, declared on the most generic superclass/interface of the declaring class
905 */
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200906 private static Method getMostGenericGetter(final Method getter) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100907 return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
908 }
909
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200910 private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100911 if(declaringClass == null) {
912 return null;
913 }
914 // Prefer interfaces
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200915 for(final Class<?> itf: declaringClass.getInterfaces()) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100916 final Method itfGetter = getMostGenericGetter(name, returnType, itf);
917 if(itfGetter != null) {
918 return itfGetter;
919 }
920 }
921 final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
922 if(superGetter != null) {
923 return superGetter;
924 }
925 if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
926 try {
927 return declaringClass.getMethod(name);
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200928 } catch(final NoSuchMethodException e) {
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100929 // Intentionally ignored, meant to fall through
930 }
931 }
932 return null;
933 }
934
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200935 private static final class AnnotatedDynamicMethod {
936 private final SingleDynamicMethod method;
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100937 /*private*/ final ValidationType validationType;
938
Attila Szegedi5ec014a2014-06-04 13:08:57 +0200939 AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200940 this.method = method;
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100941 this.validationType = validationType;
942 }
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200943
Attila Szegedi0beda5b2016-01-14 13:22:58 +0100944 MethodHandle getInvocation(final ComponentLinkRequest req) {
945 return method.getInvocation(req.getDescriptor(), req.linkerServices);
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200946 }
947
948 @SuppressWarnings("unused")
Attila Szegedi8f8c3ea2015-10-19 08:30:03 +0200949 MethodHandle getTarget(final CallSiteDescriptor desc, final LinkerServices linkerServices) {
950 final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(desc));
Attila Szegedi92bcfea2013-07-03 12:39:28 +0200951 assert inv != null;
952 return inv;
953 }
Attila Szegedi82f8cef2013-02-14 13:22:26 +0100954 }
Attila Szegediccce3fa2013-02-18 16:00:15 +0100955}