Mon, 15 Jul 2013 12:33:48 +0200
8020324: Implement Object.bindProperties(target, source) for beans
Reviewed-by: hannesw, sundar
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 */
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
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.
40 You may not use this file except in compliance with either the Apache
41 License or the BSD License.
43 If you choose to use this file in compliance with the Apache License, the
44 following notice applies to you:
46 You may obtain a copy of the Apache License at
48 http://www.apache.org/licenses/LICENSE-2.0
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.
56 If you choose to use this file in compliance with the BSD License, the
57 following notice applies to you:
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.
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 */
84 package jdk.internal.dynalink.beans;
86 import java.lang.invoke.MethodHandle;
87 import java.lang.invoke.MethodHandles;
88 import java.lang.invoke.MethodType;
89 import java.lang.reflect.AccessibleObject;
90 import java.lang.reflect.Constructor;
91 import java.lang.reflect.Field;
92 import java.lang.reflect.Member;
93 import java.lang.reflect.Method;
94 import java.lang.reflect.Modifier;
95 import java.util.Collection;
96 import java.util.Collections;
97 import java.util.HashMap;
98 import java.util.List;
99 import java.util.Map;
100 import jdk.internal.dynalink.CallSiteDescriptor;
101 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
102 import jdk.internal.dynalink.linker.GuardedInvocation;
103 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
104 import jdk.internal.dynalink.linker.LinkRequest;
105 import jdk.internal.dynalink.linker.LinkerServices;
106 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
107 import jdk.internal.dynalink.support.Guards;
108 import jdk.internal.dynalink.support.Lookup;
110 /**
111 * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
112 * exposure and method calls for both static and instance facets of a class.
113 *
114 * @author Attila Szegedi
115 */
116 abstract class AbstractJavaLinker implements GuardingDynamicLinker {
118 final Class<?> clazz;
119 private final MethodHandle classGuard;
120 private final MethodHandle assignableGuard;
121 private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();
122 private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
123 private final Map<String, DynamicMethod> methods = new HashMap<>();
125 AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard) {
126 this(clazz, classGuard, classGuard);
127 }
129 AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard, MethodHandle assignableGuard) {
130 this.clazz = clazz;
131 this.classGuard = classGuard;
132 this.assignableGuard = assignableGuard;
134 final FacetIntrospector introspector = createFacetIntrospector();
135 // Add methods and properties
136 for(Method method: introspector.getMethods()) {
137 final String name = method.getName();
138 // Add method
139 addMember(name, method, methods);
140 // Add the method as a property getter and/or setter
141 if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
142 // Property getter
143 setPropertyGetter(method, 3);
144 } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
145 method.getReturnType() == boolean.class) {
146 // Boolean property getter
147 setPropertyGetter(method, 2);
148 } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
149 // Property setter
150 addMember(decapitalize(name.substring(3)), method, propertySetters);
151 }
152 }
154 // Add field getter/setters as property getters/setters.
155 for(Field field: introspector.getFields()) {
156 final String name = field.getName();
157 // Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
158 if(!propertyGetters.containsKey(name)) {
159 setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
160 }
161 if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
162 addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),
163 propertySetters);
164 }
165 }
167 // Add inner classes, but only those for which we don't hide a property with it
168 for(Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
169 final String name = innerClassSpec.getKey();
170 if(!propertyGetters.containsKey(name)) {
171 setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
172 }
173 }
174 }
176 private static String decapitalize(String str) {
177 assert str != null;
178 if(str.isEmpty()) {
179 return str;
180 }
182 final char c0 = str.charAt(0);
183 if(Character.isLowerCase(c0)) {
184 return str;
185 }
187 // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize
188 if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {
189 return str;
190 }
192 final char c[] = str.toCharArray();
193 c[0] = Character.toLowerCase(c0);
194 return new String(c);
195 }
197 abstract FacetIntrospector createFacetIntrospector();
199 Collection<String> getReadablePropertyNames() {
200 return getUnmodifiableKeys(propertyGetters);
201 }
203 Collection<String> getWritablePropertyNames() {
204 return getUnmodifiableKeys(propertySetters);
205 }
207 Collection<String> getMethodNames() {
208 return getUnmodifiableKeys(methods);
209 }
211 private static Collection<String> getUnmodifiableKeys(Map<String, ?> m) {
212 return Collections.unmodifiableCollection(m.keySet());
213 }
215 /**
216 * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
217 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
218 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
219 * instead.
220 * @param name name of the property
221 * @param handle the method handle that implements the property getter
222 * @param validationType the validation type for the property
223 */
224 private void setPropertyGetter(String name, SingleDynamicMethod handle, ValidationType validationType) {
225 propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
226 }
228 /**
229 * Sets the specified reflective method to be the property getter for the specified property.
230 * @param getter the getter method
231 * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
232 * names starting with "is".
233 */
234 private void setPropertyGetter(Method getter, int prefixLen) {
235 setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
236 getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
237 }
239 /**
240 * Sets the specified method handle to be the property getter for the specified property. Note that you can only
241 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
242 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
243 * instead.
244 * @param name name of the property
245 * @param handle the method handle that implements the property getter
246 * @param validationType the validation type for the property
247 */
248 void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) {
249 setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
250 }
252 private void addMember(String name, AccessibleObject ao, Map<String, DynamicMethod> methodMap) {
253 addMember(name, createDynamicMethod(ao), methodMap);
254 }
256 private void addMember(String name, SingleDynamicMethod method, Map<String, DynamicMethod> methodMap) {
257 final DynamicMethod existingMethod = methodMap.get(name);
258 final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
259 if(newMethod != existingMethod) {
260 methodMap.put(name, newMethod);
261 }
262 }
264 /**
265 * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The
266 * methods should represent all overloads of the same name (or all constructors of the class).
267 * @param members the reflective members
268 * @param clazz the class declaring the reflective members
269 * @param name the common name of the reflective members.
270 * @return a dynamic method representing all the specified reflective members.
271 */
272 static DynamicMethod createDynamicMethod(Iterable<? extends AccessibleObject> members, Class<?> clazz, String name) {
273 DynamicMethod dynMethod = null;
274 for(AccessibleObject method: members) {
275 dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
276 }
277 return dynMethod;
278 }
280 /**
281 * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will
282 * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive
283 * dynamic method when needed.
284 * @param m the reflective member
285 * @return the single dynamic method representing the reflective member
286 */
287 private static SingleDynamicMethod createDynamicMethod(AccessibleObject m) {
288 if(CallerSensitiveDetector.isCallerSensitive(m)) {
289 return new CallerSensitiveDynamicMethod(m);
290 }
291 final Member member = (Member)m;
292 return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName());
293 }
295 /**
296 * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be
297 * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were
298 * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege
299 * unreflector as its caller, and thus completely useless.
300 * @param m the method or constructor
301 * @return the method handle
302 */
303 private static MethodHandle unreflectSafely(AccessibleObject m) {
304 if(m instanceof Method) {
305 final Method reflMethod = (Method)m;
306 final MethodHandle handle = SafeUnreflector.unreflect(reflMethod);
307 if(Modifier.isStatic(reflMethod.getModifiers())) {
308 return StaticClassIntrospector.editStaticMethodHandle(handle);
309 }
310 return handle;
311 }
312 return StaticClassIntrospector.editConstructorMethodHandle(SafeUnreflector.unreflectConstructor(
313 (Constructor<?>)m));
314 }
316 private static DynamicMethod mergeMethods(SingleDynamicMethod method, DynamicMethod existing, Class<?> clazz, String name) {
317 if(existing == null) {
318 return method;
319 } else if(existing.contains(method)) {
320 return existing;
321 } else if(existing instanceof SingleDynamicMethod) {
322 final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
323 odm.addMethod(((SingleDynamicMethod)existing));
324 odm.addMethod(method);
325 return odm;
326 } else if(existing instanceof OverloadedDynamicMethod) {
327 ((OverloadedDynamicMethod)existing).addMethod(method);
328 return existing;
329 }
330 throw new AssertionError();
331 }
333 @Override
334 public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices)
335 throws Exception {
336 final LinkRequest ncrequest = request.withoutRuntimeContext();
337 // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
338 final CallSiteDescriptor callSiteDescriptor = ncrequest.getCallSiteDescriptor();
339 final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR);
340 // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]).
341 if("callMethod" == op) {
342 return getCallPropWithThis(callSiteDescriptor, linkerServices);
343 }
344 List<String> operations = CallSiteDescriptorFactory.tokenizeOperators(callSiteDescriptor);
345 while(!operations.isEmpty()) {
346 final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices,
347 operations);
348 if(gic != null) {
349 return gic.getGuardedInvocation();
350 }
351 operations = pop(operations);
352 }
353 return null;
354 }
356 protected GuardedInvocationComponent getGuardedInvocationComponent(CallSiteDescriptor callSiteDescriptor,
357 LinkerServices linkerServices, List<String> operations) throws Exception {
358 if(operations.isEmpty()) {
359 return null;
360 }
361 final String op = operations.get(0);
362 // Either dyn:getProp:name(this) or dyn:getProp(this, name)
363 if("getProp".equals(op)) {
364 return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations));
365 }
366 // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value)
367 if("setProp".equals(op)) {
368 return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations));
369 }
370 // Either dyn:getMethod:name(this), or dyn:getMethod(this, name)
371 if("getMethod".equals(op)) {
372 return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations));
373 }
374 return null;
375 }
377 static final <T> List<T> pop(List<T> l) {
378 return l.subList(1, l.size());
379 }
381 MethodHandle getClassGuard(CallSiteDescriptor desc) {
382 return getClassGuard(desc.getMethodType());
383 }
385 MethodHandle getClassGuard(MethodType type) {
386 return Guards.asType(classGuard, type);
387 }
389 GuardedInvocationComponent getClassGuardedInvocationComponent(MethodHandle invocation, MethodType type) {
390 return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
391 }
393 private MethodHandle getAssignableGuard(MethodType type) {
394 return Guards.asType(assignableGuard, type);
395 }
397 private GuardedInvocation getCallPropWithThis(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
398 switch(callSiteDescriptor.getNameTokenCount()) {
399 case 3: {
400 return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
401 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
402 }
403 default: {
404 return null;
405 }
406 }
407 }
409 private GuardedInvocation createGuardedDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor,
410 LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap){
411 final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
412 return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
413 }
415 private static MethodHandle getDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor,
416 LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap) {
417 final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
418 return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
419 }
421 private static DynamicMethod getDynamicMethod(String methodName, Map<String, DynamicMethod> methodMap) {
422 final DynamicMethod dynaMethod = methodMap.get(methodName);
423 return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
424 }
426 private static SingleDynamicMethod getExplicitSignatureDynamicMethod(String methodName,
427 Map<String, DynamicMethod> methodsMap) {
428 // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
429 // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
430 // resolution works correctly in almost every situation. However, in presence of many language-specific
431 // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
432 // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
433 // for performance reasons.
435 // Is the method name lexically of the form "name(types)"?
436 final int lastChar = methodName.length() - 1;
437 if(methodName.charAt(lastChar) != ')') {
438 return null;
439 }
440 final int openBrace = methodName.indexOf('(');
441 if(openBrace == -1) {
442 return null;
443 }
445 // Find an existing method for the "name" part
446 final DynamicMethod simpleNamedMethod = methodsMap.get(methodName.substring(0, openBrace));
447 if(simpleNamedMethod == null) {
448 return null;
449 }
451 // Try to get a narrowed dynamic method for the explicit parameter types.
452 return simpleNamedMethod.getMethodForExactParamTypes(methodName.substring(openBrace + 1, lastChar));
453 }
455 private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
456 boolean.class, MethodHandle.class));
457 private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
458 MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
460 private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
461 LinkerServices linkerServices, List<String> operations) throws Exception {
462 final MethodType type = callSiteDescriptor.getMethodType();
463 switch(callSiteDescriptor.getNameTokenCount()) {
464 case 2: {
465 // Must have three arguments: target object, property name, and property value.
466 assertParameterCount(callSiteDescriptor, 3);
468 // What's below is basically:
469 // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
470 // get_setter_handle(type, linkerServices))
471 // only with a bunch of method signature adjustments. Basically, retrieve method setter
472 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
473 // component's invocation.
475 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
476 // abbreviate to R(O, N, V) going forward.
477 // We want setters that conform to "R(O, V)"
478 final MethodType setterType = type.dropParameterTypes(1, 2);
479 // Bind property setter handle to the expected setter type and linker services. Type is
480 // MethodHandle(Object, String, Object)
481 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
482 CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices);
484 // Cast getter to MethodHandle(O, N, V)
485 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
486 MethodHandle.class));
488 // Handle to invoke the setter R(MethodHandle, O, V)
489 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
490 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
491 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
492 1));
493 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
494 linkerServices, operations);
496 final MethodHandle fallbackFolded;
497 if(nextComponent == null) {
498 // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
499 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
500 type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
501 } else {
502 // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
503 // extra argument resulting from fold
504 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
505 0, MethodHandle.class);
506 }
508 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
509 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
510 IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
511 if(nextComponent == null) {
512 return getClassGuardedInvocationComponent(compositeSetter, type);
513 }
514 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
515 }
516 case 3: {
517 // Must have two arguments: target object and property value
518 assertParameterCount(callSiteDescriptor, 2);
519 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
520 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters);
521 // If we have a property setter with this name, this composite operation will always stop here
522 if(gi != null) {
523 return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
524 }
525 // If we don't have a property setter with this name, always fall back to the next operation in the
526 // composite (if any)
527 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations);
528 }
529 default: {
530 // More than two name components; don't know what to do with it.
531 return null;
532 }
533 }
534 }
536 private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
538 private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
539 boolean.class, AnnotatedDynamicMethod.class));
540 private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
541 MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
542 private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
543 "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class));
544 private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
546 private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
547 LinkerServices linkerServices, List<String> ops) throws Exception {
548 final MethodType type = callSiteDescriptor.getMethodType();
549 switch(callSiteDescriptor.getNameTokenCount()) {
550 case 2: {
551 // Must have exactly two arguments: receiver and name
552 assertParameterCount(callSiteDescriptor, 2);
554 // What's below is basically:
555 // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
556 // only with a bunch of method signature adjustments. Basically, retrieve method getter
557 // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
558 // or delegate to next component's invocation.
560 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
561 AnnotatedDynamicMethod.class));
562 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
563 GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
564 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
565 callSiteBoundMethodGetter);
566 // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
567 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
568 MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
569 // Since it's in the target of a fold, drop the unnecessary second argument
570 // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
571 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
572 type.parameterType(1));
573 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
574 linkerServices, ops);
576 final MethodHandle fallbackFolded;
577 if(nextComponent == null) {
578 // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
579 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
580 type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
581 } else {
582 // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
583 // extra argument resulting from fold
584 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
585 0, AnnotatedDynamicMethod.class);
586 }
588 // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
589 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
590 IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
591 if(nextComponent == null) {
592 return getClassGuardedInvocationComponent(compositeGetter, type);
593 }
594 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
595 }
596 case 3: {
597 // Must have exactly one argument: receiver
598 assertParameterCount(callSiteDescriptor, 1);
599 // Fixed name
600 final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
601 CallSiteDescriptor.NAME_OPERAND));
602 if(annGetter == null) {
603 // We have no such property, always delegate to the next component operation
604 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
605 }
606 final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
607 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
608 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
609 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
610 // we're linking against a field getter, don't make the assumption.
611 // NOTE: No delegation to the next component operation if we have a property with this name, even if its
612 // value is null.
613 final ValidationType validationType = annGetter.validationType;
614 // TODO: we aren't using the type that declares the most generic getter here!
615 return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
616 type), clazz, validationType);
617 }
618 default: {
619 // Can't do anything with more than 3 name components
620 return null;
621 }
622 }
623 }
625 private MethodHandle getGuard(ValidationType validationType, MethodType methodType) {
626 switch(validationType) {
627 case EXACT_CLASS: {
628 return getClassGuard(methodType);
629 }
630 case INSTANCE_OF: {
631 return getAssignableGuard(methodType);
632 }
633 case IS_ARRAY: {
634 return Guards.isArray(0, methodType);
635 }
636 case NONE: {
637 return null;
638 }
639 default: {
640 throw new AssertionError();
641 }
642 }
643 }
645 private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
646 MethodType.methodType(boolean.class, DynamicMethod.class));
647 private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
649 private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
650 LinkerServices linkerServices, List<String> ops) throws Exception {
651 final MethodType type = callSiteDescriptor.getMethodType();
652 switch(callSiteDescriptor.getNameTokenCount()) {
653 case 2: {
654 // Must have exactly two arguments: receiver and name
655 assertParameterCount(callSiteDescriptor, 2);
656 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
657 linkerServices, ops);
658 if(nextComponent == null) {
659 // No next component operation; just return a component for this operation.
660 return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
661 }
663 // What's below is basically:
664 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
665 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
666 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
668 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
669 DynamicMethod.class));
670 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
671 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
672 DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
673 DynamicMethod.class));
674 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
675 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
676 assert nextComponentInvocation.type().equals(type);
677 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
678 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
679 DynamicMethod.class);
680 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
681 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
682 IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
684 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
685 }
686 case 3: {
687 // Must have exactly one argument: receiver
688 assertParameterCount(callSiteDescriptor, 1);
689 final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken(
690 CallSiteDescriptor.NAME_OPERAND));
691 if(method == null) {
692 // We have no such method, always delegate to the next component
693 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
694 }
695 // No delegation to the next component of the composite operation; if we have a method with that name,
696 // we'll always return it at this point.
697 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
698 MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
699 }
700 default: {
701 // Can't do anything with more than 3 name components
702 return null;
703 }
704 }
705 }
707 private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
708 if(descriptor.getMethodType().parameterCount() != paramCount) {
709 throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
710 }
711 }
713 private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
714 "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
715 private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
717 /**
718 * @param id the property ID
719 * @return the method handle for retrieving the property, or null if the property does not exist
720 */
721 @SuppressWarnings("unused")
722 private Object getPropertyGetterHandle(Object id) {
723 return propertyGetters.get(id);
724 }
726 // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
727 // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
728 // a typical property setter with variable name signature (target, name, value).
729 private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
730 privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
731 LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
732 // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
733 private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
735 @SuppressWarnings("unused")
736 private MethodHandle getPropertySetterHandle(CallSiteDescriptor setterDescriptor, LinkerServices linkerServices,
737 Object id) {
738 return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
739 }
741 private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
742 "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
743 private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
745 @SuppressWarnings("unused")
746 private DynamicMethod getDynamicMethod(Object name) {
747 return getDynamicMethod(String.valueOf(name), methods);
748 }
750 /**
751 * Returns a dynamic method of the specified name.
752 *
753 * @param name name of the method
754 * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
755 * method with the specified name does not exist.
756 */
757 DynamicMethod getDynamicMethod(String name) {
758 return getDynamicMethod(name, methods);
759 }
761 /**
762 * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
763 * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
764 * creating more stable call sites.
765 * @param getter the getter
766 * @return getter with same name, declared on the most generic superclass/interface of the declaring class
767 */
768 private static Method getMostGenericGetter(Method getter) {
769 return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
770 }
772 private static Method getMostGenericGetter(String name, Class<?> returnType, Class<?> declaringClass) {
773 if(declaringClass == null) {
774 return null;
775 }
776 // Prefer interfaces
777 for(Class<?> itf: declaringClass.getInterfaces()) {
778 final Method itfGetter = getMostGenericGetter(name, returnType, itf);
779 if(itfGetter != null) {
780 return itfGetter;
781 }
782 }
783 final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
784 if(superGetter != null) {
785 return superGetter;
786 }
787 if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
788 try {
789 return declaringClass.getMethod(name);
790 } catch(NoSuchMethodException e) {
791 // Intentionally ignored, meant to fall through
792 }
793 }
794 return null;
795 }
797 private static final class AnnotatedDynamicMethod {
798 private final SingleDynamicMethod method;
799 /*private*/ final ValidationType validationType;
801 AnnotatedDynamicMethod(SingleDynamicMethod method, ValidationType validationType) {
802 this.method = method;
803 this.validationType = validationType;
804 }
806 MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
807 return method.getInvocation(callSiteDescriptor, linkerServices);
808 }
810 @SuppressWarnings("unused")
811 MethodHandle getTarget(MethodHandles.Lookup lookup) {
812 MethodHandle inv = method.getTarget(lookup);
813 assert inv != null;
814 return inv;
815 }
816 }
817 }