Thu, 24 May 2018 16:39:31 +0800
Merge
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;
109 import jdk.internal.dynalink.support.TypeUtilities;
111 /**
112 * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
113 * exposure and method calls for both static and instance facets of a class.
114 *
115 * @author Attila Szegedi
116 */
117 abstract class AbstractJavaLinker implements GuardingDynamicLinker {
119 final Class<?> clazz;
120 private final MethodHandle classGuard;
121 private final MethodHandle assignableGuard;
122 private final Map<String, AnnotatedDynamicMethod> propertyGetters = new HashMap<>();
123 private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
124 private final Map<String, DynamicMethod> methods = new HashMap<>();
126 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) {
127 this(clazz, classGuard, classGuard);
128 }
130 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) {
131 this.clazz = clazz;
132 this.classGuard = classGuard;
133 this.assignableGuard = assignableGuard;
135 final FacetIntrospector introspector = createFacetIntrospector();
136 // Add methods and properties
137 for(final Method method: introspector.getMethods()) {
138 final String name = method.getName();
139 // Add method
140 addMember(name, method, methods);
141 // Add the method as a property getter and/or setter
142 if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
143 // Property getter
144 setPropertyGetter(method, 3);
145 } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
146 method.getReturnType() == boolean.class) {
147 // Boolean property getter
148 setPropertyGetter(method, 2);
149 } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
150 // Property setter
151 addMember(decapitalize(name.substring(3)), method, propertySetters);
152 }
153 }
155 // Add field getter/setters as property getters/setters.
156 for(final Field field: introspector.getFields()) {
157 final String name = field.getName();
158 // Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
159 if(!propertyGetters.containsKey(name)) {
160 setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
161 }
162 if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
163 addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name),
164 propertySetters);
165 }
166 }
168 // Add inner classes, but only those for which we don't hide a property with it
169 for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
170 final String name = innerClassSpec.getKey();
171 if(!propertyGetters.containsKey(name)) {
172 setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
173 }
174 }
175 }
177 private static String decapitalize(final String str) {
178 assert str != null;
179 if(str.isEmpty()) {
180 return str;
181 }
183 final char c0 = str.charAt(0);
184 if(Character.isLowerCase(c0)) {
185 return str;
186 }
188 // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize
189 if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {
190 return str;
191 }
193 final char c[] = str.toCharArray();
194 c[0] = Character.toLowerCase(c0);
195 return new String(c);
196 }
198 abstract FacetIntrospector createFacetIntrospector();
200 Collection<String> getReadablePropertyNames() {
201 return getUnmodifiableKeys(propertyGetters);
202 }
204 Collection<String> getWritablePropertyNames() {
205 return getUnmodifiableKeys(propertySetters);
206 }
208 Collection<String> getMethodNames() {
209 return getUnmodifiableKeys(methods);
210 }
212 private static Collection<String> getUnmodifiableKeys(final Map<String, ?> m) {
213 return Collections.unmodifiableCollection(m.keySet());
214 }
216 /**
217 * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
218 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
219 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
220 * instead.
221 * @param name name of the property
222 * @param handle the method handle that implements the property getter
223 * @param validationType the validation type for the property
224 */
225 private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) {
226 propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
227 }
229 /**
230 * Sets the specified reflective method to be the property getter for the specified property.
231 * @param getter the getter method
232 * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
233 * names starting with "is".
234 */
235 private void setPropertyGetter(final Method getter, final int prefixLen) {
236 setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
237 getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
238 }
240 /**
241 * Sets the specified method handle to be the property getter for the specified property. Note that you can only
242 * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
243 * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)}
244 * instead.
245 * @param name name of the property
246 * @param handle the method handle that implements the property getter
247 * @param validationType the validation type for the property
248 */
249 void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) {
250 setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
251 }
253 private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) {
254 addMember(name, createDynamicMethod(ao), methodMap);
255 }
257 private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) {
258 final DynamicMethod existingMethod = methodMap.get(name);
259 final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
260 if(newMethod != existingMethod) {
261 methodMap.put(name, newMethod);
262 }
263 }
265 /**
266 * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The
267 * methods should represent all overloads of the same name (or all constructors of the class).
268 * @param members the reflective members
269 * @param clazz the class declaring the reflective members
270 * @param name the common name of the reflective members.
271 * @return a dynamic method representing all the specified reflective members.
272 */
273 static DynamicMethod createDynamicMethod(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) {
274 DynamicMethod dynMethod = null;
275 for(final AccessibleObject method: members) {
276 dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
277 }
278 return dynMethod;
279 }
281 /**
282 * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will
283 * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive
284 * dynamic method when needed.
285 * @param m the reflective member
286 * @return the single dynamic method representing the reflective member
287 */
288 private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
289 if(CallerSensitiveDetector.isCallerSensitive(m)) {
290 // Method has @CallerSensitive annotation
291 return new CallerSensitiveDynamicMethod(m);
292 }
293 // Method has no @CallerSensitive annotation
294 final MethodHandle mh;
295 try {
296 mh = unreflectSafely(m);
297 } catch (final IllegalAccessError e) {
298 // java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
299 // marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
300 return new CallerSensitiveDynamicMethod(m);
301 }
302 // Proceed with non-caller sensitive
303 final Member member = (Member)m;
304 return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
305 }
307 /**
308 * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be
309 * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were
310 * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege
311 * unreflector as its caller, and thus completely useless.
312 * @param m the method or constructor
313 * @return the method handle
314 */
315 private static MethodHandle unreflectSafely(final AccessibleObject m) {
316 if(m instanceof Method) {
317 final Method reflMethod = (Method)m;
318 final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);
319 if(Modifier.isStatic(reflMethod.getModifiers())) {
320 return StaticClassIntrospector.editStaticMethodHandle(handle);
321 }
322 return handle;
323 }
324 return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));
325 }
327 private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {
328 if(existing == null) {
329 return method;
330 } else if(existing.contains(method)) {
331 return existing;
332 } else if(existing instanceof SingleDynamicMethod) {
333 final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
334 odm.addMethod(((SingleDynamicMethod)existing));
335 odm.addMethod(method);
336 return odm;
337 } else if(existing instanceof OverloadedDynamicMethod) {
338 ((OverloadedDynamicMethod)existing).addMethod(method);
339 return existing;
340 }
341 throw new AssertionError();
342 }
344 @Override
345 public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
346 throws Exception {
347 final LinkRequest ncrequest = request.withoutRuntimeContext();
348 // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
349 final CallSiteDescriptor callSiteDescriptor = ncrequest.getCallSiteDescriptor();
350 final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR);
351 // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]).
352 if("callMethod" == op) {
353 return getCallPropWithThis(callSiteDescriptor, linkerServices);
354 }
355 List<String> operations = CallSiteDescriptorFactory.tokenizeOperators(callSiteDescriptor);
356 while(!operations.isEmpty()) {
357 final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices,
358 operations);
359 if(gic != null) {
360 return gic.getGuardedInvocation();
361 }
362 operations = pop(operations);
363 }
364 return null;
365 }
367 protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
368 final LinkerServices linkerServices, final List<String> operations) throws Exception {
369 if(operations.isEmpty()) {
370 return null;
371 }
372 final String op = operations.get(0);
373 // Either dyn:getProp:name(this) or dyn:getProp(this, name)
374 if("getProp".equals(op)) {
375 return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations));
376 }
377 // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value)
378 if("setProp".equals(op)) {
379 return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations));
380 }
381 // Either dyn:getMethod:name(this), or dyn:getMethod(this, name)
382 if("getMethod".equals(op)) {
383 return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations));
384 }
385 return null;
386 }
388 static final <T> List<T> pop(final List<T> l) {
389 return l.subList(1, l.size());
390 }
392 MethodHandle getClassGuard(final CallSiteDescriptor desc) {
393 return getClassGuard(desc.getMethodType());
394 }
396 MethodHandle getClassGuard(final MethodType type) {
397 return Guards.asType(classGuard, type);
398 }
400 GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {
401 return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
402 }
404 SingleDynamicMethod getConstructorMethod(final String signature) {
405 return null;
406 }
408 private MethodHandle getAssignableGuard(final MethodType type) {
409 return Guards.asType(assignableGuard, type);
410 }
412 private GuardedInvocation getCallPropWithThis(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
413 switch(callSiteDescriptor.getNameTokenCount()) {
414 case 3: {
415 return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
416 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
417 }
418 default: {
419 return null;
420 }
421 }
422 }
424 private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
425 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
426 final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
427 return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
428 }
430 private MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
431 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
432 final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
433 return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
434 }
436 private DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
437 final DynamicMethod dynaMethod = methodMap.get(methodName);
438 return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
439 }
441 private SingleDynamicMethod getExplicitSignatureDynamicMethod(final String fullName,
442 final Map<String, DynamicMethod> methodsMap) {
443 // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
444 // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
445 // resolution works correctly in almost every situation. However, in presence of many language-specific
446 // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
447 // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
448 // for performance reasons.
450 // Is the method name lexically of the form "name(types)"?
451 final int lastChar = fullName.length() - 1;
452 if(fullName.charAt(lastChar) != ')') {
453 return null;
454 }
455 final int openBrace = fullName.indexOf('(');
456 if(openBrace == -1) {
457 return null;
458 }
460 final String name = fullName.substring(0, openBrace);
461 final String signature = fullName.substring(openBrace + 1, lastChar);
463 // Find an existing method for the "name" part
464 final DynamicMethod simpleNamedMethod = methodsMap.get(name);
465 if(simpleNamedMethod == null) {
466 // explicit signature constructor access
467 // Java.type("java.awt.Color")["(int,int,int)"]
468 // will get Color(int,int,int) constructor of Color class.
469 if (name.isEmpty()) {
470 return getConstructorMethod(signature);
471 }
473 return null;
474 }
476 // Try to get a narrowed dynamic method for the explicit parameter types.
477 return simpleNamedMethod.getMethodForExactParamTypes(signature);
478 }
480 private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
481 boolean.class, MethodHandle.class));
482 private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
483 MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
485 private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
486 final LinkerServices linkerServices, final List<String> operations) throws Exception {
487 switch(callSiteDescriptor.getNameTokenCount()) {
488 case 2: {
489 // Must have three arguments: target object, property name, and property value.
490 assertParameterCount(callSiteDescriptor, 3);
492 // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
493 // valid for us to convert return values proactively. Also, since we don't know what setters will be
494 // invoked, we'll conservatively presume Object return type. The one exception is void return.
495 final MethodType origType = callSiteDescriptor.getMethodType();
496 final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);
498 // What's below is basically:
499 // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
500 // get_setter_handle(type, linkerServices))
501 // only with a bunch of method signature adjustments. Basically, retrieve method setter
502 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
503 // component's invocation.
505 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
506 // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
507 // Object return type).
508 final MethodType setterType = type.dropParameterTypes(1, 2);
509 // Bind property setter handle to the expected setter type and linker services. Type is
510 // MethodHandle(Object, String, Object)
511 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
512 callSiteDescriptor.changeMethodType(setterType), linkerServices);
514 // Cast getter to MethodHandle(O, N, V)
515 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
516 MethodHandle.class));
518 // Handle to invoke the setter R(MethodHandle, O, V)
519 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
520 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
521 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
522 1));
523 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
524 linkerServices, operations);
526 final MethodHandle fallbackFolded;
527 if(nextComponent == null) {
528 // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
529 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
530 type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
531 } else {
532 // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
533 // extra argument resulting from fold
534 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
535 0, MethodHandle.class);
536 }
538 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
539 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
540 IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
541 if(nextComponent == null) {
542 return getClassGuardedInvocationComponent(compositeSetter, type);
543 }
544 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
545 }
546 case 3: {
547 // Must have two arguments: target object and property value
548 assertParameterCount(callSiteDescriptor, 2);
549 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
550 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters);
551 // If we have a property setter with this name, this composite operation will always stop here
552 if(gi != null) {
553 return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
554 }
555 // If we don't have a property setter with this name, always fall back to the next operation in the
556 // composite (if any)
557 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations);
558 }
559 default: {
560 // More than two name components; don't know what to do with it.
561 return null;
562 }
563 }
564 }
566 private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
568 private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
569 boolean.class, AnnotatedDynamicMethod.class));
570 private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
571 MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
572 private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
573 "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class, LinkerServices.class));
574 private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
576 private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
577 final LinkerServices linkerServices, final List<String> ops) throws Exception {
578 switch(callSiteDescriptor.getNameTokenCount()) {
579 case 2: {
580 // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
581 // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
582 // runtime might not allow coercing at that call site.
583 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
584 // Must have exactly two arguments: receiver and name
585 assertParameterCount(callSiteDescriptor, 2);
587 // What's below is basically:
588 // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
589 // only with a bunch of method signature adjustments. Basically, retrieve method getter
590 // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
591 // or delegate to next component's invocation.
593 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
594 AnnotatedDynamicMethod.class));
595 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
596 GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup(), linkerServices);
597 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
598 callSiteBoundMethodGetter);
599 // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
600 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
601 MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
602 // Since it's in the target of a fold, drop the unnecessary second argument
603 // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
604 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
605 type.parameterType(1));
606 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
607 linkerServices, ops);
609 final MethodHandle fallbackFolded;
610 if(nextComponent == null) {
611 // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
612 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
613 type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
614 } else {
615 // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
616 // drop the extra argument resulting from fold and to change its return type to Object.
617 final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
618 final MethodType nextType = nextInvocation.type();
619 fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
620 nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
621 }
623 // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
624 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
625 IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
626 if(nextComponent == null) {
627 return getClassGuardedInvocationComponent(compositeGetter, type);
628 }
629 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
630 }
631 case 3: {
632 // Must have exactly one argument: receiver
633 assertParameterCount(callSiteDescriptor, 1);
634 // Fixed name
635 final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
636 CallSiteDescriptor.NAME_OPERAND));
637 if(annGetter == null) {
638 // We have no such property, always delegate to the next component operation
639 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
640 }
641 final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
642 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
643 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
644 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
645 // we're linking against a field getter, don't make the assumption.
646 // NOTE: No delegation to the next component operation if we have a property with this name, even if its
647 // value is null.
648 final ValidationType validationType = annGetter.validationType;
649 // TODO: we aren't using the type that declares the most generic getter here!
650 return new GuardedInvocationComponent(getter, getGuard(validationType,
651 callSiteDescriptor.getMethodType()), clazz, validationType);
652 }
653 default: {
654 // Can't do anything with more than 3 name components
655 return null;
656 }
657 }
658 }
660 private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
661 switch(validationType) {
662 case EXACT_CLASS: {
663 return getClassGuard(methodType);
664 }
665 case INSTANCE_OF: {
666 return getAssignableGuard(methodType);
667 }
668 case IS_ARRAY: {
669 return Guards.isArray(0, methodType);
670 }
671 case NONE: {
672 return null;
673 }
674 default: {
675 throw new AssertionError();
676 }
677 }
678 }
680 private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
681 MethodType.methodType(boolean.class, Object.class));
682 private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
684 private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
685 final LinkerServices linkerServices, final List<String> ops) throws Exception {
686 // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
687 // be visible outside of this linker, declare it to return Object.
688 final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
689 switch(callSiteDescriptor.getNameTokenCount()) {
690 case 2: {
691 // Must have exactly two arguments: receiver and name
692 assertParameterCount(callSiteDescriptor, 2);
693 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
694 linkerServices, ops);
695 if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
696 nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
697 // No next component operation, or it can never produce a dynamic method; just return a component
698 // for this operation.
699 return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
700 }
702 // What's below is basically:
703 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
704 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
705 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
707 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
708 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
709 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
710 OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
711 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
712 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
713 // return type.
714 assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
715 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
716 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
717 Object.class);
718 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
719 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
720 IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
722 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
723 }
724 case 3: {
725 // Must have exactly one argument: receiver
726 assertParameterCount(callSiteDescriptor, 1);
727 final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken(
728 CallSiteDescriptor.NAME_OPERAND));
729 if(method == null) {
730 // We have no such method, always delegate to the next component
731 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
732 }
733 // No delegation to the next component of the composite operation; if we have a method with that name,
734 // we'll always return it at this point.
735 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
736 MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
737 }
738 default: {
739 // Can't do anything with more than 3 name components
740 return null;
741 }
742 }
743 }
745 static class MethodPair {
746 final MethodHandle method1;
747 final MethodHandle method2;
749 MethodPair(final MethodHandle method1, final MethodHandle method2) {
750 this.method1 = method1;
751 this.method2 = method2;
752 }
754 MethodHandle guardWithTest(final MethodHandle test) {
755 return MethodHandles.guardWithTest(test, method1, method2);
756 }
757 }
759 static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {
760 final MethodType type1 = m1.type();
761 final MethodType type2 = m2.type();
762 final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
763 type2.returnType());
764 return new MethodPair(
765 m1.asType(type1.changeReturnType(commonRetType)),
766 m2.asType(type2.changeReturnType(commonRetType)));
767 }
769 private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
770 if(descriptor.getMethodType().parameterCount() != paramCount) {
771 throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
772 }
773 }
775 private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
776 "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
777 private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
779 /**
780 * @param id the property ID
781 * @return the method handle for retrieving the property, or null if the property does not exist
782 */
783 @SuppressWarnings("unused")
784 private Object getPropertyGetterHandle(final Object id) {
785 return propertyGetters.get(String.valueOf(id));
786 }
788 // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
789 // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
790 // a typical property setter with variable name signature (target, name, value).
791 private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
792 privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
793 LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
794 // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
795 private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
797 @SuppressWarnings("unused")
798 private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,
799 final Object id) {
800 return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
801 }
803 private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
804 "getDynamicMethod", Object.class, Object.class), 1, Object.class);
805 private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
807 @SuppressWarnings("unused")
808 // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
809 // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
810 // "dyn:getMethod" linking).
811 private Object getDynamicMethod(final Object name) {
812 return getDynamicMethod(String.valueOf(name), methods);
813 }
815 /**
816 * Returns a dynamic method of the specified name.
817 *
818 * @param name name of the method
819 * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
820 * method with the specified name does not exist.
821 */
822 DynamicMethod getDynamicMethod(final String name) {
823 return getDynamicMethod(name, methods);
824 }
826 /**
827 * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
828 * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
829 * creating more stable call sites.
830 * @param getter the getter
831 * @return getter with same name, declared on the most generic superclass/interface of the declaring class
832 */
833 private static Method getMostGenericGetter(final Method getter) {
834 return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
835 }
837 private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) {
838 if(declaringClass == null) {
839 return null;
840 }
841 // Prefer interfaces
842 for(final Class<?> itf: declaringClass.getInterfaces()) {
843 final Method itfGetter = getMostGenericGetter(name, returnType, itf);
844 if(itfGetter != null) {
845 return itfGetter;
846 }
847 }
848 final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
849 if(superGetter != null) {
850 return superGetter;
851 }
852 if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
853 try {
854 return declaringClass.getMethod(name);
855 } catch(final NoSuchMethodException e) {
856 // Intentionally ignored, meant to fall through
857 }
858 }
859 return null;
860 }
862 private static final class AnnotatedDynamicMethod {
863 private final SingleDynamicMethod method;
864 /*private*/ final ValidationType validationType;
866 AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {
867 this.method = method;
868 this.validationType = validationType;
869 }
871 MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
872 return method.getInvocation(callSiteDescriptor, linkerServices);
873 }
875 @SuppressWarnings("unused")
876 MethodHandle getTarget(final MethodHandles.Lookup lookup, final LinkerServices linkerServices) {
877 final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(lookup));
878 assert inv != null;
879 return inv;
880 }
881 }
882 }