Wed, 20 Aug 2014 10:25:28 +0200
8044638: Tidy up Nashorn codebase for code standards
8055199: Tidy up Nashorn codebase for code standards (August 2014)
Reviewed-by: lagergren, 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(final Class<?> clazz, final MethodHandle classGuard) {
126 this(clazz, classGuard, classGuard);
127 }
129 AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final 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(final 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(final 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(final 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(final 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(final 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(final String name, final SingleDynamicMethod handle, final 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(final Method getter, final 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(final String name, final MethodHandle handle, final ValidationType validationType) {
249 setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
250 }
252 private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) {
253 addMember(name, createDynamicMethod(ao), methodMap);
254 }
256 private void addMember(final String name, final SingleDynamicMethod method, final 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(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) {
273 DynamicMethod dynMethod = null;
274 for(final 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(final 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(final AccessibleObject m) {
304 if(m instanceof Method) {
305 final Method reflMethod = (Method)m;
306 final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);
307 if(Modifier.isStatic(reflMethod.getModifiers())) {
308 return StaticClassIntrospector.editStaticMethodHandle(handle);
309 }
310 return handle;
311 }
312 return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));
313 }
315 private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {
316 if(existing == null) {
317 return method;
318 } else if(existing.contains(method)) {
319 return existing;
320 } else if(existing instanceof SingleDynamicMethod) {
321 final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
322 odm.addMethod(((SingleDynamicMethod)existing));
323 odm.addMethod(method);
324 return odm;
325 } else if(existing instanceof OverloadedDynamicMethod) {
326 ((OverloadedDynamicMethod)existing).addMethod(method);
327 return existing;
328 }
329 throw new AssertionError();
330 }
332 @Override
333 public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
334 throws Exception {
335 final LinkRequest ncrequest = request.withoutRuntimeContext();
336 // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
337 final CallSiteDescriptor callSiteDescriptor = ncrequest.getCallSiteDescriptor();
338 final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR);
339 // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]).
340 if("callMethod" == op) {
341 return getCallPropWithThis(callSiteDescriptor, linkerServices);
342 }
343 List<String> operations = CallSiteDescriptorFactory.tokenizeOperators(callSiteDescriptor);
344 while(!operations.isEmpty()) {
345 final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices,
346 operations);
347 if(gic != null) {
348 return gic.getGuardedInvocation();
349 }
350 operations = pop(operations);
351 }
352 return null;
353 }
355 protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
356 final LinkerServices linkerServices, final List<String> operations) throws Exception {
357 if(operations.isEmpty()) {
358 return null;
359 }
360 final String op = operations.get(0);
361 // Either dyn:getProp:name(this) or dyn:getProp(this, name)
362 if("getProp".equals(op)) {
363 return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations));
364 }
365 // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value)
366 if("setProp".equals(op)) {
367 return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations));
368 }
369 // Either dyn:getMethod:name(this), or dyn:getMethod(this, name)
370 if("getMethod".equals(op)) {
371 return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations));
372 }
373 return null;
374 }
376 static final <T> List<T> pop(final List<T> l) {
377 return l.subList(1, l.size());
378 }
380 MethodHandle getClassGuard(final CallSiteDescriptor desc) {
381 return getClassGuard(desc.getMethodType());
382 }
384 MethodHandle getClassGuard(final MethodType type) {
385 return Guards.asType(classGuard, type);
386 }
388 GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {
389 return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
390 }
392 private MethodHandle getAssignableGuard(final MethodType type) {
393 return Guards.asType(assignableGuard, type);
394 }
396 private GuardedInvocation getCallPropWithThis(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
397 switch(callSiteDescriptor.getNameTokenCount()) {
398 case 3: {
399 return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
400 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
401 }
402 default: {
403 return null;
404 }
405 }
406 }
408 private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
409 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
410 final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
411 return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
412 }
414 private static MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
415 final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
416 final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
417 return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
418 }
420 private static DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
421 final DynamicMethod dynaMethod = methodMap.get(methodName);
422 return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
423 }
425 private static SingleDynamicMethod getExplicitSignatureDynamicMethod(final String methodName,
426 final Map<String, DynamicMethod> methodsMap) {
427 // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
428 // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
429 // resolution works correctly in almost every situation. However, in presence of many language-specific
430 // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
431 // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload
432 // for performance reasons.
434 // Is the method name lexically of the form "name(types)"?
435 final int lastChar = methodName.length() - 1;
436 if(methodName.charAt(lastChar) != ')') {
437 return null;
438 }
439 final int openBrace = methodName.indexOf('(');
440 if(openBrace == -1) {
441 return null;
442 }
444 // Find an existing method for the "name" part
445 final DynamicMethod simpleNamedMethod = methodsMap.get(methodName.substring(0, openBrace));
446 if(simpleNamedMethod == null) {
447 return null;
448 }
450 // Try to get a narrowed dynamic method for the explicit parameter types.
451 return simpleNamedMethod.getMethodForExactParamTypes(methodName.substring(openBrace + 1, lastChar));
452 }
454 private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
455 boolean.class, MethodHandle.class));
456 private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
457 MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
459 private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
460 final LinkerServices linkerServices, final List<String> operations) throws Exception {
461 final MethodType type = callSiteDescriptor.getMethodType();
462 switch(callSiteDescriptor.getNameTokenCount()) {
463 case 2: {
464 // Must have three arguments: target object, property name, and property value.
465 assertParameterCount(callSiteDescriptor, 3);
467 // What's below is basically:
468 // foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
469 // get_setter_handle(type, linkerServices))
470 // only with a bunch of method signature adjustments. Basically, retrieve method setter
471 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
472 // component's invocation.
474 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
475 // abbreviate to R(O, N, V) going forward.
476 // We want setters that conform to "R(O, V)"
477 final MethodType setterType = type.dropParameterTypes(1, 2);
478 // Bind property setter handle to the expected setter type and linker services. Type is
479 // MethodHandle(Object, String, Object)
480 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
481 CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices);
483 // Cast getter to MethodHandle(O, N, V)
484 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
485 MethodHandle.class));
487 // Handle to invoke the setter R(MethodHandle, O, V)
488 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
489 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
490 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
491 1));
492 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
493 linkerServices, operations);
495 final MethodHandle fallbackFolded;
496 if(nextComponent == null) {
497 // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
498 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
499 type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
500 } else {
501 // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
502 // extra argument resulting from fold
503 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
504 0, MethodHandle.class);
505 }
507 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
508 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
509 IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
510 if(nextComponent == null) {
511 return getClassGuardedInvocationComponent(compositeSetter, type);
512 }
513 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
514 }
515 case 3: {
516 // Must have two arguments: target object and property value
517 assertParameterCount(callSiteDescriptor, 2);
518 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
519 callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters);
520 // If we have a property setter with this name, this composite operation will always stop here
521 if(gi != null) {
522 return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
523 }
524 // If we don't have a property setter with this name, always fall back to the next operation in the
525 // composite (if any)
526 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations);
527 }
528 default: {
529 // More than two name components; don't know what to do with it.
530 return null;
531 }
532 }
533 }
535 private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
537 private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
538 boolean.class, AnnotatedDynamicMethod.class));
539 private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
540 MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
541 private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
542 "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class));
543 private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
545 private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
546 final LinkerServices linkerServices, final List<String> ops) throws Exception {
547 final MethodType type = callSiteDescriptor.getMethodType();
548 switch(callSiteDescriptor.getNameTokenCount()) {
549 case 2: {
550 // Must have exactly two arguments: receiver and name
551 assertParameterCount(callSiteDescriptor, 2);
553 // What's below is basically:
554 // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
555 // only with a bunch of method signature adjustments. Basically, retrieve method getter
556 // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
557 // or delegate to next component's invocation.
559 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
560 AnnotatedDynamicMethod.class));
561 final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
562 GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
563 final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
564 callSiteBoundMethodGetter);
565 // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
566 final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
567 MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
568 // Since it's in the target of a fold, drop the unnecessary second argument
569 // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
570 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
571 type.parameterType(1));
572 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
573 linkerServices, ops);
575 final MethodHandle fallbackFolded;
576 if(nextComponent == null) {
577 // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
578 fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
579 type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
580 } else {
581 // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
582 // extra argument resulting from fold
583 fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
584 0, AnnotatedDynamicMethod.class);
585 }
587 // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
588 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
589 IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
590 if(nextComponent == null) {
591 return getClassGuardedInvocationComponent(compositeGetter, type);
592 }
593 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
594 }
595 case 3: {
596 // Must have exactly one argument: receiver
597 assertParameterCount(callSiteDescriptor, 1);
598 // Fixed name
599 final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
600 CallSiteDescriptor.NAME_OPERAND));
601 if(annGetter == null) {
602 // We have no such property, always delegate to the next component operation
603 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
604 }
605 final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
606 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
607 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
608 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
609 // we're linking against a field getter, don't make the assumption.
610 // NOTE: No delegation to the next component operation if we have a property with this name, even if its
611 // value is null.
612 final ValidationType validationType = annGetter.validationType;
613 // TODO: we aren't using the type that declares the most generic getter here!
614 return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
615 type), clazz, validationType);
616 }
617 default: {
618 // Can't do anything with more than 3 name components
619 return null;
620 }
621 }
622 }
624 private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
625 switch(validationType) {
626 case EXACT_CLASS: {
627 return getClassGuard(methodType);
628 }
629 case INSTANCE_OF: {
630 return getAssignableGuard(methodType);
631 }
632 case IS_ARRAY: {
633 return Guards.isArray(0, methodType);
634 }
635 case NONE: {
636 return null;
637 }
638 default: {
639 throw new AssertionError();
640 }
641 }
642 }
644 private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
645 MethodType.methodType(boolean.class, DynamicMethod.class));
646 private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
648 private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
649 final LinkerServices linkerServices, final List<String> ops) throws Exception {
650 final MethodType type = callSiteDescriptor.getMethodType();
651 switch(callSiteDescriptor.getNameTokenCount()) {
652 case 2: {
653 // Must have exactly two arguments: receiver and name
654 assertParameterCount(callSiteDescriptor, 2);
655 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
656 linkerServices, ops);
657 if(nextComponent == null) {
658 // No next component operation; just return a component for this operation.
659 return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
660 }
662 // What's below is basically:
663 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
664 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
665 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
667 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
668 DynamicMethod.class));
669 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
670 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
671 DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
672 DynamicMethod.class));
673 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
674 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
675 assert nextComponentInvocation.type().equals(type);
676 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
677 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
678 DynamicMethod.class);
679 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
680 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
681 IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
683 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
684 }
685 case 3: {
686 // Must have exactly one argument: receiver
687 assertParameterCount(callSiteDescriptor, 1);
688 final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken(
689 CallSiteDescriptor.NAME_OPERAND));
690 if(method == null) {
691 // We have no such method, always delegate to the next component
692 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
693 }
694 // No delegation to the next component of the composite operation; if we have a method with that name,
695 // we'll always return it at this point.
696 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
697 MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
698 }
699 default: {
700 // Can't do anything with more than 3 name components
701 return null;
702 }
703 }
704 }
706 private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
707 if(descriptor.getMethodType().parameterCount() != paramCount) {
708 throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
709 }
710 }
712 private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
713 "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
714 private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
716 /**
717 * @param id the property ID
718 * @return the method handle for retrieving the property, or null if the property does not exist
719 */
720 @SuppressWarnings("unused")
721 private Object getPropertyGetterHandle(final Object id) {
722 return propertyGetters.get(id);
723 }
725 // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
726 // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
727 // a typical property setter with variable name signature (target, name, value).
728 private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
729 privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class,
730 LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
731 // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
732 private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
734 @SuppressWarnings("unused")
735 private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,
736 final Object id) {
737 return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
738 }
740 private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
741 "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
742 private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
744 @SuppressWarnings("unused")
745 private DynamicMethod getDynamicMethod(final Object name) {
746 return getDynamicMethod(String.valueOf(name), methods);
747 }
749 /**
750 * Returns a dynamic method of the specified name.
751 *
752 * @param name name of the method
753 * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
754 * method with the specified name does not exist.
755 */
756 DynamicMethod getDynamicMethod(final String name) {
757 return getDynamicMethod(name, methods);
758 }
760 /**
761 * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
762 * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
763 * creating more stable call sites.
764 * @param getter the getter
765 * @return getter with same name, declared on the most generic superclass/interface of the declaring class
766 */
767 private static Method getMostGenericGetter(final Method getter) {
768 return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
769 }
771 private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) {
772 if(declaringClass == null) {
773 return null;
774 }
775 // Prefer interfaces
776 for(final Class<?> itf: declaringClass.getInterfaces()) {
777 final Method itfGetter = getMostGenericGetter(name, returnType, itf);
778 if(itfGetter != null) {
779 return itfGetter;
780 }
781 }
782 final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
783 if(superGetter != null) {
784 return superGetter;
785 }
786 if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
787 try {
788 return declaringClass.getMethod(name);
789 } catch(final NoSuchMethodException e) {
790 // Intentionally ignored, meant to fall through
791 }
792 }
793 return null;
794 }
796 private static final class AnnotatedDynamicMethod {
797 private final SingleDynamicMethod method;
798 /*private*/ final ValidationType validationType;
800 AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {
801 this.method = method;
802 this.validationType = validationType;
803 }
805 MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
806 return method.getInvocation(callSiteDescriptor, linkerServices);
807 }
809 @SuppressWarnings("unused")
810 MethodHandle getTarget(final MethodHandles.Lookup lookup) {
811 final MethodHandle inv = method.getTarget(lookup);
812 assert inv != null;
813 return inv;
814 }
815 }
816 }