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

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

author
attila
date
Wed, 20 Aug 2014 10:25:28 +0200
changeset 962
ac62e33a99b0
parent 830
3cb09c560108
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.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 GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
   169             final LinkerServices linkerServices, final List<String> operations) throws Exception {
   170         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   171         final Class<?> declaredType = callSiteType.parameterType(0);
   172         final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
   173                 linkerServices, operations);
   175         // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing
   176         // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're
   177         // dealing with an array, or a list or map, but hey...
   178         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
   179         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
   180         final GuardedInvocationComponent gic;
   181         final boolean isMap;
   182         if(declaredType.isArray()) {
   183             gic = new GuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType));
   184             isMap = false;
   185         } else if(List.class.isAssignableFrom(declaredType)) {
   186             gic = new GuardedInvocationComponent(GET_LIST_ELEMENT);
   187             isMap = false;
   188         } else if(Map.class.isAssignableFrom(declaredType)) {
   189             gic = new GuardedInvocationComponent(GET_MAP_ELEMENT);
   190             isMap = true;
   191         } else if(clazz.isArray()) {
   192             gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementGetter(clazz), callSiteType);
   193             isMap = false;
   194         } else if(List.class.isAssignableFrom(clazz)) {
   195             gic = new GuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class,
   196                     ValidationType.INSTANCE_OF);
   197             isMap = false;
   198         } else if(Map.class.isAssignableFrom(clazz)) {
   199             gic = new GuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class,
   200                     ValidationType.INSTANCE_OF);
   201             isMap = true;
   202         } else {
   203             // Can't retrieve elements for objects that are neither arrays, nor list, nor maps.
   204             return nextComponent;
   205         }
   207         // We can have "dyn:getElem:foo", especially in composites, i.e. "dyn:getElem|getProp|getMethod:foo"
   208         final String fixedKey = getFixedKey(callSiteDescriptor);
   209         // Convert the key to a number if we're working with a list or array
   210         final Object typedFixedKey;
   211         if(!isMap && fixedKey != null) {
   212             typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
   213             if(typedFixedKey == null) {
   214                 // key is not numeric, it can never succeed
   215                 return nextComponent;
   216             }
   217         } else {
   218             typedFixedKey = fixedKey;
   219         }
   221         final GuardedInvocation gi = gic.getGuardedInvocation();
   222         final Binder binder = new Binder(linkerServices, callSiteType, typedFixedKey);
   223         final MethodHandle invocation = gi.getInvocation();
   225         if(nextComponent == null) {
   226             return gic.replaceInvocation(binder.bind(invocation));
   227         }
   229         final MethodHandle checkGuard;
   230         if(invocation == GET_LIST_ELEMENT) {
   231             checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
   232         } else if(invocation == GET_MAP_ELEMENT) {
   233             // TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it
   234             // into a GWT that tests if it returned null, and if it did, do another GWT with containsKey()
   235             // that returns constant null (on true), or falls back to next component (on false)
   236             checkGuard = CONTAINS_MAP;
   237         } else {
   238             checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
   239         }
   240         return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
   241                 binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
   242                 gic.getValidatorClass(), gic.getValidationType());
   243     }
   245     private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) {
   246         return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken(
   247                 CallSiteDescriptor.NAME_OPERAND);
   248     }
   250     private static Object convertKeyToInteger(final String fixedKey, final LinkerServices linkerServices) throws Exception {
   251         try {
   252             if(linkerServices.canConvert(String.class, Number.class)) {
   253                 try {
   254                     final Object val = linkerServices.getTypeConverter(String.class, Number.class).invoke(fixedKey);
   255                     if(!(val instanceof Number)) {
   256                         return null; // not a number
   257                     }
   258                     final Number n = (Number)val;
   259                     if(n instanceof Integer) {
   260                         return n;
   261                     }
   262                     final int intIndex = n.intValue();
   263                     final double doubleValue = n.doubleValue();
   264                     if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinites trigger IOOBE
   265                         return null; // not an exact integer
   266                     }
   267                     return Integer.valueOf(intIndex);
   268                 } catch(Exception|Error e) {
   269                     throw e;
   270                 } catch(final Throwable t) {
   271                     throw new RuntimeException(t);
   272                 }
   273             }
   274             return Integer.valueOf(fixedKey);
   275         } catch(final NumberFormatException e) {
   276             // key is not a number
   277             return null;
   278         }
   279     }
   281     private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
   282         final Class<?> sourceType = desc.getMethodType().parameterType(1);
   283         if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
   284             return mh;
   285         } else if(ls.canConvert(sourceType, Number.class)) {
   286             final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
   287             return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
   288                     mh.type().parameterType(1))));
   289         }
   290         return mh;
   291     }
   293     /**
   294      * Contains methods to adapt an item getter/setter method handle to the requested type, optionally binding it to a
   295      * fixed key first.
   296      * @author Attila Szegedi
   297      * @version $Id: $
   298      */
   299     private static class Binder {
   300         private final LinkerServices linkerServices;
   301         private final MethodType methodType;
   302         private final Object fixedKey;
   304         Binder(final LinkerServices linkerServices, final MethodType methodType, final Object fixedKey) {
   305             this.linkerServices = linkerServices;
   306             this.methodType = fixedKey == null ? methodType : methodType.insertParameterTypes(1, fixedKey.getClass());
   307             this.fixedKey = fixedKey;
   308         }
   310         /*private*/ MethodHandle bind(final MethodHandle handle) {
   311             return bindToFixedKey(linkerServices.asType(handle, methodType));
   312         }
   314         /*private*/ MethodHandle bindTest(final MethodHandle handle) {
   315             return bindToFixedKey(Guards.asType(handle, methodType));
   316         }
   318         private MethodHandle bindToFixedKey(final MethodHandle handle) {
   319             return fixedKey == null ? handle : MethodHandles.insertArguments(handle, 1, fixedKey);
   320         }
   321     }
   323     private static MethodHandle RANGE_CHECK_ARRAY = findRangeCheck(Object.class);
   324     private static MethodHandle RANGE_CHECK_LIST = findRangeCheck(List.class);
   325     private static MethodHandle CONTAINS_MAP = Lookup.PUBLIC.findVirtual(Map.class, "containsKey",
   326             MethodType.methodType(boolean.class, Object.class));
   328     private static MethodHandle findRangeCheck(final Class<?> collectionType) {
   329         return Lookup.findOwnStatic(MethodHandles.lookup(), "rangeCheck", boolean.class, collectionType, Object.class);
   330     }
   332     @SuppressWarnings("unused")
   333     private static final boolean rangeCheck(final Object array, final Object index) {
   334         if(!(index instanceof Number)) {
   335             return false;
   336         }
   337         final Number n = (Number)index;
   338         final int intIndex = n.intValue();
   339         final double doubleValue = n.doubleValue();
   340         if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinite trigger IOOBE
   341             return false;
   342         }
   343         if(0 <= intIndex && intIndex < Array.getLength(array)) {
   344             return true;
   345         }
   346         throw new ArrayIndexOutOfBoundsException("Array index out of range: " + n);
   347     }
   349     @SuppressWarnings("unused")
   350     private static final boolean rangeCheck(final List<?> list, final Object index) {
   351         if(!(index instanceof Number)) {
   352             return false;
   353         }
   354         final Number n = (Number)index;
   355         final int intIndex = n.intValue();
   356         final double doubleValue = n.doubleValue();
   357         if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinite trigger IOOBE
   358             return false;
   359         }
   360         if(0 <= intIndex && intIndex < list.size()) {
   361             return true;
   362         }
   363         throw new IndexOutOfBoundsException("Index: " + n + ", Size: " + list.size());
   364     }
   366     private static MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set",
   367             MethodType.methodType(Object.class, int.class, Object.class));
   369     private static MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put",
   370             MethodType.methodType(Object.class, Object.class, Object.class));
   372     private GuardedInvocationComponent getElementSetter(final CallSiteDescriptor callSiteDescriptor,
   373             final LinkerServices linkerServices, final List<String> operations) throws Exception {
   374         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   375         final Class<?> declaredType = callSiteType.parameterType(0);
   377         final GuardedInvocationComponent gic;
   378         // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing
   379         // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're
   380         // dealing with an array, or a list or map, but hey...
   381         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
   382         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
   383         final boolean isMap;
   384         if(declaredType.isArray()) {
   385             gic = new GuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType));
   386             isMap = false;
   387         } else if(List.class.isAssignableFrom(declaredType)) {
   388             gic = new GuardedInvocationComponent(SET_LIST_ELEMENT);
   389             isMap = false;
   390         } else if(Map.class.isAssignableFrom(declaredType)) {
   391             gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT);
   392             isMap = true;
   393         } else if(clazz.isArray()) {
   394             gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementSetter(clazz), callSiteType);
   395             isMap = false;
   396         } else if(List.class.isAssignableFrom(clazz)) {
   397             gic = new GuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class,
   398                     ValidationType.INSTANCE_OF);
   399             isMap = false;
   400         } else if(Map.class.isAssignableFrom(clazz)) {
   401             gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class,
   402                     ValidationType.INSTANCE_OF);
   403             isMap = true;
   404         } else {
   405             // Can't set elements for objects that are neither arrays, nor list, nor maps.
   406             gic = null;
   407             isMap = false;
   408         }
   410         // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map,
   411         // as maps will always succeed in setting the element and will never need to fall back to the next component
   412         // operation.
   413         final GuardedInvocationComponent nextComponent = isMap ? null : getGuardedInvocationComponent(
   414                 callSiteDescriptor, linkerServices, operations);
   415         if(gic == null) {
   416             return nextComponent;
   417         }
   419         // We can have "dyn:setElem:foo", especially in composites, i.e. "dyn:setElem|setProp:foo"
   420         final String fixedKey = getFixedKey(callSiteDescriptor);
   421         // Convert the key to a number if we're working with a list or array
   422         final Object typedFixedKey;
   423         if(!isMap && fixedKey != null) {
   424             typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
   425             if(typedFixedKey == null) {
   426                 // key is not numeric, it can never succeed
   427                 return nextComponent;
   428             }
   429         } else {
   430             typedFixedKey = fixedKey;
   431         }
   433         final GuardedInvocation gi = gic.getGuardedInvocation();
   434         final Binder binder = new Binder(linkerServices, callSiteType, typedFixedKey);
   435         final MethodHandle invocation = gi.getInvocation();
   437         if(nextComponent == null) {
   438             return gic.replaceInvocation(binder.bind(invocation));
   439         }
   441         final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
   442             RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
   443         return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
   444                 binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
   445                 gic.getValidatorClass(), gic.getValidationType());
   446     }
   448     private static MethodHandle GET_ARRAY_LENGTH = Lookup.PUBLIC.findStatic(Array.class, "getLength",
   449             MethodType.methodType(int.class, Object.class));
   451     private static MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size",
   452             MethodType.methodType(int.class));
   454     private static MethodHandle GET_MAP_LENGTH = Lookup.PUBLIC.findVirtual(Map.class, "size",
   455             MethodType.methodType(int.class));
   457     private static MethodHandle COLLECTION_GUARD = Guards.getInstanceOfGuard(Collection.class);
   459     private GuardedInvocationComponent getLengthGetter(final CallSiteDescriptor callSiteDescriptor) {
   460         assertParameterCount(callSiteDescriptor, 1);
   461         final MethodType callSiteType = callSiteDescriptor.getMethodType();
   462         final Class<?> declaredType = callSiteType.parameterType(0);
   463         // If declared type of receiver at the call site is already an array, collection, or map, bind without guard.
   464         // Thing is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance
   465         // they're dealing with an array, collection, or map, but hey...
   466         if(declaredType.isArray()) {
   467             return new GuardedInvocationComponent(GET_ARRAY_LENGTH.asType(callSiteType));
   468         } else if(Collection.class.isAssignableFrom(declaredType)) {
   469             return new GuardedInvocationComponent(GET_COLLECTION_LENGTH.asType(callSiteType));
   470         } else if(Map.class.isAssignableFrom(declaredType)) {
   471             return new GuardedInvocationComponent(GET_MAP_LENGTH.asType(callSiteType));
   472         }
   474         // Otherwise, create a binding based on the actual type of the argument with an appropriate guard.
   475         if(clazz.isArray()) {
   476             return new GuardedInvocationComponent(GET_ARRAY_LENGTH.asType(callSiteType), Guards.isArray(0,
   477                     callSiteType), ValidationType.IS_ARRAY);
   478         } if(Collection.class.isAssignableFrom(clazz)) {
   479             return new GuardedInvocationComponent(GET_COLLECTION_LENGTH.asType(callSiteType), Guards.asType(
   480                     COLLECTION_GUARD, callSiteType), Collection.class, ValidationType.INSTANCE_OF);
   481         } if(Map.class.isAssignableFrom(clazz)) {
   482             return new GuardedInvocationComponent(GET_MAP_LENGTH.asType(callSiteType), Guards.asType(MAP_GUARD,
   483                     callSiteType), Map.class, ValidationType.INSTANCE_OF);
   484         }
   485         // Can't retrieve length for objects that are neither arrays, nor collections, nor maps.
   486         return null;
   487     }
   489     private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
   490         if(descriptor.getMethodType().parameterCount() != paramCount) {
   491             throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
   492         }
   493     }
   494 }

mercurial