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

Sat, 07 Nov 2020 10:30:02 +0800

author
aoqi
date
Sat, 07 Nov 2020 10:30:02 +0800
changeset 2605
afbc625eaca7
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Added tag mips-jdk8u275-b01 for changeset 7c756d901f9a

     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.Array;
    90 import java.util.Collection;
    91 import java.util.List;
    92 import java.util.Map;
    93 import jdk.internal.dynalink.CallSiteDescriptor;
    94 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
    95 import jdk.internal.dynalink.linker.GuardedInvocation;
    96 import jdk.internal.dynalink.linker.LinkerServices;
    97 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
    98 import jdk.internal.dynalink.support.Guards;
    99 import jdk.internal.dynalink.support.Lookup;
   100 import jdk.internal.dynalink.support.TypeUtilities;
   102 /**
   103  * A class that provides linking capabilities for a single POJO class. Normally not used directly, but managed by
   104  * {@link BeansLinker}.
   105  *
   106  * @author Attila Szegedi
   107  */
   108 class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker {
   109     BeanLinker(final Class<?> clazz) {
   110         super(clazz, Guards.getClassGuard(clazz), Guards.getInstanceOfGuard(clazz));
   111         if(clazz.isArray()) {
   112             // Some languages won't have a notion of manipulating collections. Exposing "length" on arrays as an
   113             // explicit property is beneficial for them.
   114             // REVISIT: is it maybe a code smell that "dyn:getLength" is not needed?
   115             setPropertyGetter("length", GET_ARRAY_LENGTH, ValidationType.IS_ARRAY);
   116         } else if(List.class.isAssignableFrom(clazz)) {
   117             setPropertyGetter("length", GET_COLLECTION_LENGTH, ValidationType.INSTANCE_OF);
   118         }
   119     }
   121     @Override
   122     public boolean canLinkType(final Class<?> type) {
   123         return type == clazz;
   124     }
   126     @Override
   127     FacetIntrospector createFacetIntrospector() {
   128         return new BeanIntrospector(clazz);
   129     }
   131     @Override
   132     protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
   133             final LinkerServices linkerServices, final List<String> operations) throws Exception {
   134         final GuardedInvocationComponent superGic = super.getGuardedInvocationComponent(callSiteDescriptor,
   135                 linkerServices, operations);
   136         if(superGic != null) {
   137             return superGic;
   138         }
   139         if(operations.isEmpty()) {
   140             return null;
   141         }
   142         final String op = operations.get(0);
   143         // dyn:getElem(this, id)
   144         // id is typically either an int (for arrays and lists) or an object (for maps). linkerServices can provide
   145         // conversion from call site argument type though.
   146         if("getElem".equals(op)) {
   147             return getElementGetter(callSiteDescriptor, linkerServices, pop(operations));
   148         }
   149         if("setElem".equals(op)) {
   150             return getElementSetter(callSiteDescriptor, linkerServices, pop(operations));
   151         }
   152         // dyn:getLength(this) (works on Java arrays, collections, and maps)
   153         if("getLength".equals(op)) {
   154             return getLengthGetter(callSiteDescriptor);
   155         }
   156         return null;
   157     }
   159     private static MethodHandle GET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "get",
   160             MethodType.methodType(Object.class, int.class));
   162     private static MethodHandle GET_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "get",
   163             MethodType.methodType(Object.class, Object.class));
   165     private static MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class);
   166     private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class);
   168     private enum CollectionType {
   169         ARRAY, LIST, MAP
   170     };
   172     private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
   173             final LinkerServices linkerServices, final List<String> operations) throws Exception {
   174         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   175         final Class<?> declaredType = callSiteType.parameterType(0);
   176         final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
   177                 linkerServices, operations);
   179         // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing
   180         // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're
   181         // dealing with an array, or a list or map, but hey...
   182         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
   183         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
   184         final GuardedInvocationComponent gic;
   185         final CollectionType collectionType;
   186         if(declaredType.isArray()) {
   187             gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType), linkerServices);
   188             collectionType = CollectionType.ARRAY;
   189         } else if(List.class.isAssignableFrom(declaredType)) {
   190             gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, linkerServices);
   191             collectionType = CollectionType.LIST;
   192         } else if(Map.class.isAssignableFrom(declaredType)) {
   193             gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, linkerServices);
   194             collectionType = CollectionType.MAP;
   195         } else if(clazz.isArray()) {
   196             gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(MethodHandles.arrayElementGetter(clazz)), callSiteType);
   197             collectionType = CollectionType.ARRAY;
   198         } else if(List.class.isAssignableFrom(clazz)) {
   199             gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
   200                     linkerServices);
   201             collectionType = CollectionType.LIST;
   202         } else if(Map.class.isAssignableFrom(clazz)) {
   203             gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, ValidationType.INSTANCE_OF,
   204                     linkerServices);
   205             collectionType = CollectionType.MAP;
   206         } else {
   207             // Can't retrieve elements for objects that are neither arrays, nor list, nor maps.
   208             return nextComponent;
   209         }
   211         // We can have "dyn:getElem:foo", especially in composites, i.e. "dyn:getElem|getProp|getMethod:foo"
   212         final String fixedKey = getFixedKey(callSiteDescriptor);
   213         // Convert the key to a number if we're working with a list or array
   214         final Object typedFixedKey;
   215         if(collectionType != CollectionType.MAP && fixedKey != null) {
   216             typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
   217             if(typedFixedKey == null) {
   218                 // key is not numeric, it can never succeed
   219                 return nextComponent;
   220             }
   221         } else {
   222             typedFixedKey = fixedKey;
   223         }
   225         final GuardedInvocation gi = gic.getGuardedInvocation();
   226         final Binder binder = new Binder(linkerServices, callSiteType, typedFixedKey);
   227         final MethodHandle invocation = gi.getInvocation();
   229         if(nextComponent == null) {
   230             return gic.replaceInvocation(binder.bind(invocation));
   231         }
   233         final MethodHandle checkGuard;
   234         switch(collectionType) {
   235         case LIST:
   236             checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
   237             break;
   238         case MAP:
   239             // TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it
   240             // into a GWT that tests if it returned null, and if it did, do another GWT with containsKey()
   241             // that returns constant null (on true), or falls back to next component (on false)
   242             checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP);
   243             break;
   244         case ARRAY:
   245             checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
   246             break;
   247         default:
   248             throw new AssertionError();
   249         }
   250         final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
   251                 nextComponent.getGuardedInvocation().getInvocation());
   252         return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
   253                 gic.getValidatorClass(), gic.getValidationType());
   254     }
   256     private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
   257             final MethodHandle invocation, final LinkerServices linkerServices) {
   258         return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation));
   259     }
   261     private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
   262             final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass,
   263             final ValidationType validationType, final LinkerServices linkerServices) {
   264         return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation), guard,
   265                 validatorClass, validationType);
   266     }
   268     private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) {
   269         return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken(
   270                 CallSiteDescriptor.NAME_OPERAND);
   271     }
   273     private static Object convertKeyToInteger(final String fixedKey, final LinkerServices linkerServices) throws Exception {
   274         try {
   275             if(linkerServices.canConvert(String.class, Number.class)) {
   276                 try {
   277                     final Object val = linkerServices.getTypeConverter(String.class, Number.class).invoke(fixedKey);
   278                     if(!(val instanceof Number)) {
   279                         return null; // not a number
   280                     }
   281                     final Number n = (Number)val;
   282                     if(n instanceof Integer) {
   283                         return n;
   284                     }
   285                     final int intIndex = n.intValue();
   286                     final double doubleValue = n.doubleValue();
   287                     if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinites trigger IOOBE
   288                         return null; // not an exact integer
   289                     }
   290                     return Integer.valueOf(intIndex);
   291                 } catch(Exception|Error e) {
   292                     throw e;
   293                 } catch(final Throwable t) {
   294                     throw new RuntimeException(t);
   295                 }
   296             }
   297             return Integer.valueOf(fixedKey);
   298         } catch(final NumberFormatException e) {
   299             // key is not a number
   300             return null;
   301         }
   302     }
   304     private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
   305         final Class<?> sourceType = desc.getMethodType().parameterType(1);
   306         if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
   307             return mh;
   308         } else if(ls.canConvert(sourceType, Number.class)) {
   309             final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
   310             return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
   311                     mh.type().parameterType(1))));
   312         }
   313         return mh;
   314     }
   316     /**
   317      * Contains methods to adapt an item getter/setter method handle to the requested type, optionally binding it to a
   318      * fixed key first.
   319      * @author Attila Szegedi
   320      * @version $Id: $
   321      */
   322     private static class Binder {
   323         private final LinkerServices linkerServices;
   324         private final MethodType methodType;
   325         private final Object fixedKey;
   327         Binder(final LinkerServices linkerServices, final MethodType methodType, final Object fixedKey) {
   328             this.linkerServices = linkerServices;
   329             this.methodType = fixedKey == null ? methodType : methodType.insertParameterTypes(1, fixedKey.getClass());
   330             this.fixedKey = fixedKey;
   331         }
   333         /*private*/ MethodHandle bind(final MethodHandle handle) {
   334             return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType));
   335         }
   337         /*private*/ MethodHandle bindTest(final MethodHandle handle) {
   338             return bindToFixedKey(Guards.asType(handle, methodType));
   339         }
   341         private MethodHandle bindToFixedKey(final MethodHandle handle) {
   342             return fixedKey == null ? handle : MethodHandles.insertArguments(handle, 1, fixedKey);
   343         }
   344     }
   346     private static MethodHandle RANGE_CHECK_ARRAY = findRangeCheck(Object.class);
   347     private static MethodHandle RANGE_CHECK_LIST = findRangeCheck(List.class);
   348     private static MethodHandle CONTAINS_MAP = Lookup.PUBLIC.findVirtual(Map.class, "containsKey",
   349             MethodType.methodType(boolean.class, Object.class));
   351     private static MethodHandle findRangeCheck(final Class<?> collectionType) {
   352         return Lookup.findOwnStatic(MethodHandles.lookup(), "rangeCheck", boolean.class, collectionType, Object.class);
   353     }
   355     @SuppressWarnings("unused")
   356     private static final boolean rangeCheck(final Object array, final Object index) {
   357         if(!(index instanceof Number)) {
   358             return false;
   359         }
   360         final Number n = (Number)index;
   361         final int intIndex = n.intValue();
   362         final double doubleValue = n.doubleValue();
   363         if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinite trigger IOOBE
   364             return false;
   365         }
   366         if(0 <= intIndex && intIndex < Array.getLength(array)) {
   367             return true;
   368         }
   369         throw new ArrayIndexOutOfBoundsException("Array index out of range: " + n);
   370     }
   372     @SuppressWarnings("unused")
   373     private static final boolean rangeCheck(final List<?> list, final Object index) {
   374         if(!(index instanceof Number)) {
   375             return false;
   376         }
   377         final Number n = (Number)index;
   378         final int intIndex = n.intValue();
   379         final double doubleValue = n.doubleValue();
   380         if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinite trigger IOOBE
   381             return false;
   382         }
   383         if(0 <= intIndex && intIndex < list.size()) {
   384             return true;
   385         }
   386         throw new IndexOutOfBoundsException("Index: " + n + ", Size: " + list.size());
   387     }
   389     private static MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set",
   390             MethodType.methodType(Object.class, int.class, Object.class));
   392     private static MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put",
   393             MethodType.methodType(Object.class, Object.class, Object.class));
   395     private GuardedInvocationComponent getElementSetter(final CallSiteDescriptor callSiteDescriptor,
   396             final LinkerServices linkerServices, final List<String> operations) throws Exception {
   397         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   398         final Class<?> declaredType = callSiteType.parameterType(0);
   400         final GuardedInvocationComponent gic;
   401         // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing
   402         // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're
   403         // dealing with an array, or a list or map, but hey...
   404         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
   405         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
   406         final CollectionType collectionType;
   407         if(declaredType.isArray()) {
   408             gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType), linkerServices);
   409             collectionType = CollectionType.ARRAY;
   410         } else if(List.class.isAssignableFrom(declaredType)) {
   411             gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, linkerServices);
   412             collectionType = CollectionType.LIST;
   413         } else if(Map.class.isAssignableFrom(declaredType)) {
   414             gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, linkerServices);
   415             collectionType = CollectionType.MAP;
   416         } else if(clazz.isArray()) {
   417             gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(
   418                     MethodHandles.arrayElementSetter(clazz)), callSiteType);
   419             collectionType = CollectionType.ARRAY;
   420         } else if(List.class.isAssignableFrom(clazz)) {
   421             gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
   422                     linkerServices);
   423             collectionType = CollectionType.LIST;
   424         } else if(Map.class.isAssignableFrom(clazz)) {
   425             gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType),
   426                     Map.class, ValidationType.INSTANCE_OF, linkerServices);
   427             collectionType = CollectionType.MAP;
   428         } else {
   429             // Can't set elements for objects that are neither arrays, nor list, nor maps.
   430             gic = null;
   431             collectionType = null;
   432         }
   434         // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map,
   435         // as maps will always succeed in setting the element and will never need to fall back to the next component
   436         // operation.
   437         final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getGuardedInvocationComponent(
   438                 callSiteDescriptor, linkerServices, operations);
   439         if(gic == null) {
   440             return nextComponent;
   441         }
   443         // We can have "dyn:setElem:foo", especially in composites, i.e. "dyn:setElem|setProp:foo"
   444         final String fixedKey = getFixedKey(callSiteDescriptor);
   445         // Convert the key to a number if we're working with a list or array
   446         final Object typedFixedKey;
   447         if(collectionType != CollectionType.MAP && fixedKey != null) {
   448             typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
   449             if(typedFixedKey == null) {
   450                 // key is not numeric, it can never succeed
   451                 return nextComponent;
   452             }
   453         } else {
   454             typedFixedKey = fixedKey;
   455         }
   457         final GuardedInvocation gi = gic.getGuardedInvocation();
   458         final Binder binder = new Binder(linkerServices, callSiteType, typedFixedKey);
   459         final MethodHandle invocation = gi.getInvocation();
   461         if(nextComponent == null) {
   462             return gic.replaceInvocation(binder.bind(invocation));
   463         }
   465         assert collectionType == CollectionType.LIST || collectionType == CollectionType.ARRAY;
   466         final MethodHandle checkGuard = convertArgToInt(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST :
   467             RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
   468         final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
   469                 nextComponent.getGuardedInvocation().getInvocation());
   470         return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
   471                 gic.getValidatorClass(), gic.getValidationType());
   472     }
   474     private static MethodHandle GET_ARRAY_LENGTH = Lookup.PUBLIC.findStatic(Array.class, "getLength",
   475             MethodType.methodType(int.class, Object.class));
   477     private static MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size",
   478             MethodType.methodType(int.class));
   480     private static MethodHandle GET_MAP_LENGTH = Lookup.PUBLIC.findVirtual(Map.class, "size",
   481             MethodType.methodType(int.class));
   483     private static MethodHandle COLLECTION_GUARD = Guards.getInstanceOfGuard(Collection.class);
   485     private GuardedInvocationComponent getLengthGetter(final CallSiteDescriptor callSiteDescriptor) {
   486         assertParameterCount(callSiteDescriptor, 1);
   487         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   488         final Class<?> declaredType = callSiteType.parameterType(0);
   489         // If declared type of receiver at the call site is already an array, collection, or map, bind without guard.
   490         // Thing is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance
   491         // they're dealing with an array, collection, or map, but hey...
   492         if(declaredType.isArray()) {
   493             return new GuardedInvocationComponent(GET_ARRAY_LENGTH.asType(callSiteType));
   494         } else if(Collection.class.isAssignableFrom(declaredType)) {
   495             return new GuardedInvocationComponent(GET_COLLECTION_LENGTH.asType(callSiteType));
   496         } else if(Map.class.isAssignableFrom(declaredType)) {
   497             return new GuardedInvocationComponent(GET_MAP_LENGTH.asType(callSiteType));
   498         }
   500         // Otherwise, create a binding based on the actual type of the argument with an appropriate guard.
   501         if(clazz.isArray()) {
   502             return new GuardedInvocationComponent(GET_ARRAY_LENGTH.asType(callSiteType), Guards.isArray(0,
   503                     callSiteType), ValidationType.IS_ARRAY);
   504         } if(Collection.class.isAssignableFrom(clazz)) {
   505             return new GuardedInvocationComponent(GET_COLLECTION_LENGTH.asType(callSiteType), Guards.asType(
   506                     COLLECTION_GUARD, callSiteType), Collection.class, ValidationType.INSTANCE_OF);
   507         } if(Map.class.isAssignableFrom(clazz)) {
   508             return new GuardedInvocationComponent(GET_MAP_LENGTH.asType(callSiteType), Guards.asType(MAP_GUARD,
   509                     callSiteType), Map.class, ValidationType.INSTANCE_OF);
   510         }
   511         // Can't retrieve length for objects that are neither arrays, nor collections, nor maps.
   512         return null;
   513     }
   515     private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
   516         if(descriptor.getMethodType().parameterCount() != paramCount) {
   517             throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
   518         }
   519     }
   520 }

mercurial