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

Wed, 20 Aug 2014 10:25:28 +0200

author
attila
date
Wed, 20 Aug 2014 10:25:28 +0200
changeset 962
ac62e33a99b0
parent 524
badc919cd621
child 963
e2497b11a021
permissions
-rw-r--r--

8044638: Tidy up Nashorn codebase for code standards
8055199: Tidy up Nashorn codebase for code standards (August 2014)
Reviewed-by: lagergren, sundar

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

mercurial