src/jdk/internal/dynalink/beans/AbstractJavaLinker.java

Mon, 15 Jul 2013 12:33:48 +0200

author
attila
date
Mon, 15 Jul 2013 12:33:48 +0200
changeset 439
973d78ee0728
parent 404
18d467e94150
child 463
8b97fe2b7c98
permissions
-rw-r--r--

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 }

mercurial