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

Wed, 27 Feb 2013 15:20:26 +0100

author
attila
date
Wed, 27 Feb 2013 15:20:26 +0100
changeset 123
071e859b371e
parent 101
f8221ce53c2e
child 404
18d467e94150
permissions
-rw-r--r--

8009143: Eliminate Dynalink dependency on java.beans
Reviewed-by: jlaskey, lagergren, sundar

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 /*
    27  * This file is available under and governed by the GNU General Public
    28  * License version 2 only, as published by the Free Software Foundation.
    29  * However, the following notice accompanied the original version of this
    30  * file, and Oracle licenses the original version of this file under the BSD
    31  * license:
    32  */
    33 /*
    34    Copyright 2009-2013 Attila Szegedi
    36    Licensed under both the Apache License, Version 2.0 (the "Apache License")
    37    and the BSD License (the "BSD License"), with licensee being free to
    38    choose either of the two at their discretion.
    40    You may not use this file except in compliance with either the Apache
    41    License or the BSD License.
    43    If you choose to use this file in compliance with the Apache License, the
    44    following notice applies to you:
    46        You may obtain a copy of the Apache License at
    48            http://www.apache.org/licenses/LICENSE-2.0
    50        Unless required by applicable law or agreed to in writing, software
    51        distributed under the License is distributed on an "AS IS" BASIS,
    52        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    53        implied. See the License for the specific language governing
    54        permissions and limitations under the License.
    56    If you choose to use this file in compliance with the BSD License, the
    57    following notice applies to you:
    59        Redistribution and use in source and binary forms, with or without
    60        modification, are permitted provided that the following conditions are
    61        met:
    62        * Redistributions of source code must retain the above copyright
    63          notice, this list of conditions and the following disclaimer.
    64        * Redistributions in binary form must reproduce the above copyright
    65          notice, this list of conditions and the following disclaimer in the
    66          documentation and/or other materials provided with the distribution.
    67        * Neither the name of the copyright holder nor the names of
    68          contributors may be used to endorse or promote products derived from
    69          this software without specific prior written permission.
    71        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    72        IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    73        TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    74        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
    75        BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    76        CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    77        SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
    78        BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    79        WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
    80        OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    81        ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    82 */
    84 package jdk.internal.dynalink.beans;
    86 import java.lang.invoke.MethodHandle;
    87 import java.lang.invoke.MethodHandles;
    88 import java.lang.invoke.MethodType;
    89 import java.lang.reflect.Field;
    90 import java.lang.reflect.Method;
    91 import java.lang.reflect.Modifier;
    92 import java.util.HashMap;
    93 import java.util.List;
    94 import java.util.Map;
    95 import jdk.internal.dynalink.CallSiteDescriptor;
    96 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
    97 import jdk.internal.dynalink.linker.GuardedInvocation;
    98 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
    99 import jdk.internal.dynalink.linker.LinkRequest;
   100 import jdk.internal.dynalink.linker.LinkerServices;
   101 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
   102 import jdk.internal.dynalink.support.Guards;
   103 import jdk.internal.dynalink.support.Lookup;
   105 /**
   106  * A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
   107  * exposure and method calls for both static and instance facets of a class.
   108  *
   109  * @author Attila Szegedi
   110  */
   111 abstract class AbstractJavaLinker implements GuardingDynamicLinker {
   112     final Class<?> clazz;
   113     private final MethodHandle classGuard;
   114     private final MethodHandle assignableGuard;
   115     private final Map<String, AnnotatedMethodHandle> propertyGetters = new HashMap<>();
   116     private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
   117     private final Map<String, DynamicMethod> methods = new HashMap<>();
   119     AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard) {
   120         this(clazz, classGuard, classGuard);
   121     }
   123     AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard, MethodHandle assignableGuard) {
   124         this.clazz = clazz;
   125         this.classGuard = classGuard;
   126         this.assignableGuard = assignableGuard;
   128         final FacetIntrospector introspector = createFacetIntrospector();
   129         // Add methods and properties
   130         for(Method method: introspector.getMethods()) {
   131             final String name = method.getName();
   132             final MethodHandle methodHandle = introspector.unreflect(method);
   133             // Add method
   134             addMember(name, methodHandle, methods);
   135             // Add the method as a property getter and/or setter
   136             if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) {
   137                 // Property getter
   138                 setPropertyGetter(decapitalize(name.substring(3)), introspector.unreflect(
   139                         getMostGenericGetter(method)), ValidationType.INSTANCE_OF);
   140             } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 &&
   141                     method.getReturnType() == boolean.class) {
   142                 // Boolean property getter
   143                 setPropertyGetter(decapitalize(name.substring(2)), introspector.unreflect(
   144                         getMostGenericGetter(method)), ValidationType.INSTANCE_OF);
   145             } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) {
   146                 // Property setter
   147                 addMember(decapitalize(name.substring(3)), methodHandle, propertySetters);
   148             }
   149         }
   151         // Add field getter/setters as property getters/setters.
   152         for(Field field: introspector.getFields()) {
   153             final String name = field.getName();
   154             // Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
   155             if(!propertyGetters.containsKey(name)) {
   156                 setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS);
   157             }
   158             if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) {
   159                 addMember(name, introspector.unreflectSetter(field), propertySetters);
   160             }
   161         }
   163         // Add inner classes, but only those for which we don't hide a property with it
   164         for(Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
   165             final String name = innerClassSpec.getKey();
   166             if(!propertyGetters.containsKey(name)) {
   167                 setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
   168             }
   169         }
   170     }
   172     private static String decapitalize(String str) {
   173         assert str != null;
   174         if(str.isEmpty()) {
   175             return str;
   176         }
   178         final char c0 = str.charAt(0);
   179         if(Character.isLowerCase(c0)) {
   180             return str;
   181         }
   183         // If it has two consecutive upper-case characters, i.e. "URL", don't decapitalize
   184         if(str.length() > 1 && Character.isUpperCase(str.charAt(1))) {
   185             return str;
   186         }
   188         final char c[] = str.toCharArray();
   189         c[0] = Character.toLowerCase(c0);
   190         return new String(c);
   191     }
   193     abstract FacetIntrospector createFacetIntrospector();
   195     void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) {
   196         propertyGetters.put(name, new AnnotatedMethodHandle(handle, validationType));
   197     }
   199     private void addMember(String name, MethodHandle mh, Map<String, DynamicMethod> methodMap) {
   200         final DynamicMethod existingMethod = methodMap.get(name);
   201         final DynamicMethod newMethod = addMember(mh, existingMethod, clazz, name);
   202         if(newMethod != existingMethod) {
   203             methodMap.put(name, newMethod);
   204         }
   205     }
   207     static DynamicMethod createDynamicMethod(Iterable<MethodHandle> methodHandles, Class<?> clazz, String name) {
   208         DynamicMethod dynMethod = null;
   209         for(MethodHandle methodHandle: methodHandles) {
   210             dynMethod = addMember(methodHandle, dynMethod, clazz, name);
   211         }
   212         return dynMethod;
   213     }
   215     private static DynamicMethod addMember(MethodHandle mh, DynamicMethod existing, Class<?> clazz, String name) {
   216         if(existing == null) {
   217             return new SimpleDynamicMethod(mh, clazz, name);
   218         } else if(existing.contains(mh)) {
   219             return existing;
   220         } else if(existing instanceof SimpleDynamicMethod) {
   221             final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name);
   222             odm.addMethod(((SimpleDynamicMethod)existing));
   223             odm.addMethod(mh);
   224             return odm;
   225         } else if(existing instanceof OverloadedDynamicMethod) {
   226             ((OverloadedDynamicMethod)existing).addMethod(mh);
   227             return existing;
   228         }
   229         throw new AssertionError();
   230     }
   232     @Override
   233     public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices)
   234             throws Exception {
   235         final LinkRequest ncrequest = request.withoutRuntimeContext();
   236         // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
   237         final CallSiteDescriptor callSiteDescriptor = ncrequest.getCallSiteDescriptor();
   238         final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR);
   239         // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]).
   240         if("callMethod" == op) {
   241             return getCallPropWithThis(callSiteDescriptor, linkerServices);
   242         }
   243         List<String> operations = CallSiteDescriptorFactory.tokenizeOperators(callSiteDescriptor);
   244         while(!operations.isEmpty()) {
   245             final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices,
   246                     operations);
   247             if(gic != null) {
   248                 return gic.getGuardedInvocation();
   249             }
   250             operations = pop(operations);
   251         }
   252         return null;
   253     }
   255     protected GuardedInvocationComponent getGuardedInvocationComponent(CallSiteDescriptor callSiteDescriptor,
   256             LinkerServices linkerServices, List<String> operations) throws Exception {
   257         if(operations.isEmpty()) {
   258             return null;
   259         }
   260         final String op = operations.get(0);
   261         // Either dyn:getProp:name(this) or dyn:getProp(this, name)
   262         if("getProp".equals(op)) {
   263             return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations));
   264         }
   265         // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value)
   266         if("setProp".equals(op)) {
   267             return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations));
   268         }
   269         // Either dyn:getMethod:name(this), or dyn:getMethod(this, name)
   270         if("getMethod".equals(op)) {
   271             return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations));
   272         }
   273         return null;
   274     }
   276     static final <T> List<T> pop(List<T> l) {
   277         return l.subList(1, l.size());
   278     }
   280     MethodHandle getClassGuard(CallSiteDescriptor desc) {
   281         return getClassGuard(desc.getMethodType());
   282     }
   284     MethodHandle getClassGuard(MethodType type) {
   285         return Guards.asType(classGuard, type);
   286     }
   288     GuardedInvocationComponent getClassGuardedInvocationComponent(MethodHandle invocation, MethodType type) {
   289         return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
   290     }
   292     private MethodHandle getAssignableGuard(MethodType type) {
   293         return Guards.asType(assignableGuard, type);
   294     }
   296     private GuardedInvocation getCallPropWithThis(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
   297         switch(callSiteDescriptor.getNameTokenCount()) {
   298             case 3: {
   299                 return createGuardedDynamicMethodInvocation(callSiteDescriptor.getMethodType(), linkerServices,
   300                         callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
   301             }
   302             default: {
   303                 return null;
   304             }
   305         }
   306     }
   308     private GuardedInvocation createGuardedDynamicMethodInvocation(MethodType callSiteType,
   309             LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap){
   310         final MethodHandle inv = getDynamicMethodInvocation(callSiteType, linkerServices, methodName, methodMap);
   311         return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteType));
   312     }
   314     private static MethodHandle getDynamicMethodInvocation(MethodType callSiteType, LinkerServices linkerServices,
   315             String methodName, Map<String, DynamicMethod> methodMap) {
   316         final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
   317         return dynaMethod != null ? dynaMethod.getInvocation(callSiteType, linkerServices) : null;
   318     }
   320     private static DynamicMethod getDynamicMethod(String methodName, Map<String, DynamicMethod> methodMap) {
   321         final DynamicMethod dynaMethod = methodMap.get(methodName);
   322         return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
   323     }
   325     private static SimpleDynamicMethod getExplicitSignatureDynamicMethod(String methodName,
   326             Map<String, DynamicMethod> methodsMap) {
   327         // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
   328         // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
   329         // resolution works correctly in almost every situation. However, in presence of many language-specific
   330         // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected
   331         // at invocation time, so a programmer knowledgable of the situation might choose to pin down an exact overload
   332         // for performance reasons.
   334         // Is the method name lexically of the form "name(types)"?
   335         final int lastChar = methodName.length() - 1;
   336         if(methodName.charAt(lastChar) != ')') {
   337             return null;
   338         }
   339         final int openBrace = methodName.indexOf('(');
   340         if(openBrace == -1) {
   341             return null;
   342         }
   344         // Find an existing method for the "name" part
   345         final DynamicMethod simpleNamedMethod = methodsMap.get(methodName.substring(0, openBrace));
   346         if(simpleNamedMethod == null) {
   347             return null;
   348         }
   350         // Try to get a narrowed dynamic method for the explicit parameter types.
   351         return simpleNamedMethod.getMethodForExactParamTypes(methodName.substring(openBrace + 1, lastChar));
   352     }
   354     private static final MethodHandle IS_METHOD_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
   355             boolean.class, MethodHandle.class));
   356     private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
   357             MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
   359     private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
   360             LinkerServices linkerServices, List<String> operations) throws Exception {
   361         final MethodType type = callSiteDescriptor.getMethodType();
   362         switch(callSiteDescriptor.getNameTokenCount()) {
   363             case 2: {
   364                 // Must have three arguments: target object, property name, and property value.
   365                 assertParameterCount(callSiteDescriptor, 3);
   367                 // What's below is basically:
   368                 //   foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
   369                 //     get_setter_handle(type, linkerServices))
   370                 // only with a bunch of method signature adjustments. Basically, retrieve method setter
   371                 // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
   372                 // component's invocation.
   374                 // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
   375                 // abbreviate to R(O, N, V) going forward.
   376                 // We want setters that conform to "R(O, V)"
   377                 final MethodType setterType = type.dropParameterTypes(1, 2);
   378                 // Bind property setter handle to the expected setter type and linker services. Type is
   379                 // MethodHandle(Object, String, Object)
   380                 final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, setterType,
   381                         linkerServices);
   383                 // Cast getter to MethodHandle(O, N, V)
   384                 final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
   385                         MethodHandle.class));
   387                 // Handle to invoke the setter R(MethodHandle, O, V)
   388                 final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
   389                 // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
   390                 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
   391                         1));
   392                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
   393                         linkerServices, operations);
   395                 final MethodHandle fallbackFolded;
   396                 if(nextComponent == null) {
   397                     // Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
   398                     fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
   399                             type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
   400                 } else {
   401                     // R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
   402                     // extra argument resulting from fold
   403                     fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
   404                             0, MethodHandle.class);
   405                 }
   407                 // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
   408                 final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
   409                             IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
   410                 if(nextComponent == null) {
   411                     return getClassGuardedInvocationComponent(compositeSetter, type);
   412                 }
   413                 return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
   414             }
   415             case 3: {
   416                 // Must have two arguments: target object and property value
   417                 assertParameterCount(callSiteDescriptor, 2);
   418                 final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor.getMethodType(),
   419                         linkerServices, callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND),
   420                         propertySetters);
   421                 // If we have a property setter with this name, this composite operation will always stop here
   422                 if(gi != null) {
   423                     return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
   424                 }
   425                 // If we don't have a property setter with this name, always fall back to the next operation in the
   426                 // composite (if any)
   427                 return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations);
   428             }
   429             default: {
   430                 // More than two name components; don't know what to do with it.
   431                 return null;
   432             }
   433         }
   434     }
   436     private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
   438     private static final MethodHandle IS_ANNOTATED_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType(
   439             boolean.class, AnnotatedMethodHandle.class));
   440     private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_HANDLE = MethodHandles.dropArguments(
   441             MethodHandles.constant(Object.class, null), 0, AnnotatedMethodHandle.class);
   442     private static final MethodHandle GET_ANNOTATED_HANDLE = privateLookup.findGetter(AnnotatedMethodHandle.class,
   443             "handle", MethodHandle.class);
   444     private static final MethodHandle GENERIC_PROPERTY_GETTER_HANDLER_INVOKER = MethodHandles.filterArguments(
   445             MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)), 0, GET_ANNOTATED_HANDLE);
   447     private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
   448             LinkerServices linkerServices, List<String> ops) throws Exception {
   449         final MethodType type = callSiteDescriptor.getMethodType();
   450         switch(callSiteDescriptor.getNameTokenCount()) {
   451             case 2: {
   452                 // Must have exactly two arguments: receiver and name
   453                 assertParameterCount(callSiteDescriptor, 2);
   455                 // What's below is basically:
   456                 //   foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
   457                 // only with a bunch of method signature adjustments. Basically, retrieve method getter
   458                 // AnnotatedMethodHandle; if it is non-null, invoke its "handle" field, otherwise either return null,
   459                 // or delegate to next component's invocation.
   461                 final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
   462                         AnnotatedMethodHandle.class));
   463                 // Object(AnnotatedMethodHandle, Object)->R(AnnotatedMethodHandle, T0)
   464                 final MethodHandle invokeHandleTyped = linkerServices.asType(GENERIC_PROPERTY_GETTER_HANDLER_INVOKER,
   465                         MethodType.methodType(type.returnType(), AnnotatedMethodHandle.class, type.parameterType(0)));
   466                 // Since it's in the target of a fold, drop the unnecessary second argument
   467                 // R(AnnotatedMethodHandle, T0)->R(AnnotatedMethodHandle, T0, T1)
   468                 final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
   469                         type.parameterType(1));
   470                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
   471                         linkerServices, ops);
   473                 final MethodHandle fallbackFolded;
   474                 if(nextComponent == null) {
   475                     // Object(AnnotatedMethodHandle)->R(AnnotatedMethodHandle, T0, T1); returns constant null
   476                     fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_HANDLE, 1,
   477                             type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedMethodHandle.class));
   478                 } else {
   479                     // R(T0, T1)->R(AnnotatedMethodHAndle, T0, T1); adapts the next component's invocation to drop the
   480                     // extra argument resulting from fold
   481                     fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
   482                             0, AnnotatedMethodHandle.class);
   483                 }
   485                 // fold(R(AnnotatedMethodHandle, T0, T1), AnnotatedMethodHandle(T0, T1))
   486                 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
   487                             IS_ANNOTATED_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
   488                 if(nextComponent == null) {
   489                     return getClassGuardedInvocationComponent(compositeGetter, type);
   490                 }
   491                 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
   492             }
   493             case 3: {
   494                 // Must have exactly one argument: receiver
   495                 assertParameterCount(callSiteDescriptor, 1);
   496                 // Fixed name
   497                 final AnnotatedMethodHandle annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
   498                         CallSiteDescriptor.NAME_OPERAND));
   499                 if(annGetter == null) {
   500                     // We have no such property, always delegate to the next component operation
   501                     return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
   502                 }
   503                 final MethodHandle getter = annGetter.handle;
   504                 // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
   505                 // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
   506                 // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
   507                 // we're linking against a field getter, don't make the assumption.
   508                 // NOTE: No delegation to the next component operation if we have a property with this name, even if its
   509                 // value is null.
   510                 final ValidationType validationType = annGetter.validationType;
   511                 return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
   512                         type), clazz, validationType);
   513             }
   514             default: {
   515                 // Can't do anything with more than 3 name components
   516                 return null;
   517             }
   518         }
   519     }
   521     private MethodHandle getGuard(ValidationType validationType, MethodType methodType) {
   522         switch(validationType) {
   523             case EXACT_CLASS: {
   524                 return getClassGuard(methodType);
   525             }
   526             case INSTANCE_OF: {
   527                 return getAssignableGuard(methodType);
   528             }
   529             case IS_ARRAY: {
   530                 return Guards.isArray(0, methodType);
   531             }
   532             case NONE: {
   533                 return null;
   534             }
   535             default: {
   536                 throw new AssertionError();
   537             }
   538         }
   539     }
   541     private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
   542             MethodType.methodType(boolean.class, DynamicMethod.class));
   543     private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
   545     private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
   546             LinkerServices linkerServices, List<String> ops) throws Exception {
   547         final MethodType type = callSiteDescriptor.getMethodType();
   548         switch(callSiteDescriptor.getNameTokenCount()) {
   549             case 2: {
   550                 // Must have exactly two arguments: receiver and name
   551                 assertParameterCount(callSiteDescriptor, 2);
   552                 final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
   553                         linkerServices, ops);
   554                 if(nextComponent == null) {
   555                     // No next component operation; just return a component for this operation.
   556                     return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
   557                 }
   559                 // What's below is basically:
   560                 // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
   561                 // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
   562                 // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
   564                 final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
   565                         DynamicMethod.class));
   566                 // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
   567                 final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
   568                         DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
   569                                 DynamicMethod.class));
   570                 final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
   571                 // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
   572                 assert nextComponentInvocation.type().equals(type);
   573                 // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
   574                 final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
   575                         DynamicMethod.class);
   576                 // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
   577                 final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
   578                         IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
   580                 return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
   581             }
   582             case 3: {
   583                 // Must have exactly one argument: receiver
   584                 assertParameterCount(callSiteDescriptor, 1);
   585                 final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken(
   586                         CallSiteDescriptor.NAME_OPERAND));
   587                 if(method == null) {
   588                     // We have no such method, always delegate to the next component
   589                     return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
   590                 }
   591                 // No delegation to the next component of the composite operation; if we have a method with that name,
   592                 // we'll always return it at this point.
   593                 return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
   594                         MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
   595             }
   596             default: {
   597                 // Can't do anything with more than 3 name components
   598                 return null;
   599             }
   600         }
   601     }
   603     private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
   604         if(descriptor.getMethodType().parameterCount() != paramCount) {
   605             throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
   606         }
   607     }
   609     private static MethodHandle GET_PROPERTY_GETTER_HANDLE = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
   610             "getPropertyGetterHandle", Object.class, Object.class), 1, Object.class);
   611     private final MethodHandle getPropertyGetterHandle = GET_PROPERTY_GETTER_HANDLE.bindTo(this);
   613     /**
   614      * @param id the property ID
   615      * @return the method handle for retrieving the property, or null if the property does not exist
   616      */
   617     @SuppressWarnings("unused")
   618     private Object getPropertyGetterHandle(Object id) {
   619         return propertyGetters.get(id);
   620     }
   622     // Type is MethodHandle(BeanLinker, MethodType, LinkerServices, Object, String, Object), of which the two "Object"
   623     // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is
   624     // a typical property setter with variable name signature (target, name, value).
   625     private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments(
   626             privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, MethodType.class,
   627                     LinkerServices.class, Object.class), 3, Object.class), 5, Object.class);
   628     // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object)
   629     private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
   631     @SuppressWarnings("unused")
   632     private MethodHandle getPropertySetterHandle(MethodType setterType, LinkerServices linkerServices, Object id) {
   633         return getDynamicMethodInvocation(setterType, linkerServices, String.valueOf(id), propertySetters);
   634     }
   636     private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
   637             "getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
   638     private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
   640     @SuppressWarnings("unused")
   641     private DynamicMethod getDynamicMethod(Object name) {
   642         return getDynamicMethod(String.valueOf(name), methods);
   643     }
   645     /**
   646      * Returns a dynamic method of the specified name.
   647      *
   648      * @param name name of the method
   649      * @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
   650      * method with the specified name does not exist.
   651      */
   652     DynamicMethod getDynamicMethod(String name) {
   653         return getDynamicMethod(name, methods);
   654     }
   656     /**
   657      * Find the most generic superclass that declares this getter. Since getters have zero args (aside from the
   658      * receiver), they can't be overloaded, so we're free to link with an instanceof guard for the most generic one,
   659      * creating more stable call sites.
   660      * @param getter the getter
   661      * @return getter with same name, declared on the most generic superclass/interface of the declaring class
   662      */
   663     private static Method getMostGenericGetter(Method getter) {
   664         return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
   665     }
   667     private static Method getMostGenericGetter(String name, Class<?> returnType, Class<?> declaringClass) {
   668         if(declaringClass == null) {
   669             return null;
   670         }
   671         // Prefer interfaces
   672         for(Class<?> itf: declaringClass.getInterfaces()) {
   673             final Method itfGetter = getMostGenericGetter(name, returnType, itf);
   674             if(itfGetter != null) {
   675                 return itfGetter;
   676             }
   677         }
   678         final Method superGetter = getMostGenericGetter(name, returnType, declaringClass.getSuperclass());
   679         if(superGetter != null) {
   680             return superGetter;
   681         }
   682         if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
   683             try {
   684                 return declaringClass.getMethod(name);
   685             } catch(NoSuchMethodException e) {
   686                 // Intentionally ignored, meant to fall through
   687             }
   688         }
   689         return null;
   690     }
   692     private static final class AnnotatedMethodHandle {
   693         final MethodHandle handle;
   694         /*private*/ final ValidationType validationType;
   696         AnnotatedMethodHandle(MethodHandle handle, ValidationType validationType) {
   697             this.handle = handle;
   698             this.validationType = validationType;
   699         }
   700     }
   701 }

mercurial