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

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1651
86ed55da74ab
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

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 }

mercurial