1/* 2 * Copyright (c) 2010, 2016, 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 */ 25 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 35 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. 39 40 You may not use this file except in compliance with either the Apache 41 License or the BSD License. 42 43 If you choose to use this file in compliance with the Apache License, the 44 following notice applies to you: 45 46 You may obtain a copy of the Apache License at 47 48 http://www.apache.org/licenses/LICENSE-2.0 49 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. 55 56 If you choose to use this file in compliance with the BSD License, the 57 following notice applies to you: 58 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. 70 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*/ 83 84package jdk.dynalink.beans; 85 86import java.lang.invoke.MethodHandle; 87import java.lang.invoke.MethodHandles; 88import java.lang.invoke.MethodType; 89import java.lang.reflect.Array; 90import java.util.Collection; 91import java.util.Collections; 92import java.util.List; 93import java.util.Map; 94import jdk.dynalink.CallSiteDescriptor; 95import jdk.dynalink.Namespace; 96import jdk.dynalink.Operation; 97import jdk.dynalink.StandardNamespace; 98import jdk.dynalink.StandardOperation; 99import jdk.dynalink.beans.GuardedInvocationComponent.ValidationType; 100import jdk.dynalink.linker.GuardedInvocation; 101import jdk.dynalink.linker.LinkerServices; 102import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker; 103import jdk.dynalink.linker.support.Guards; 104import jdk.dynalink.linker.support.Lookup; 105import jdk.dynalink.linker.support.TypeUtilities; 106 107/** 108 * A class that provides linking capabilities for a single POJO class. Normally not used directly, but managed by 109 * {@link BeansLinker}. 110 */ 111class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker { 112 BeanLinker(final Class<?> clazz) { 113 super(clazz, Guards.getClassGuard(clazz), Guards.getInstanceOfGuard(clazz)); 114 if(clazz.isArray()) { 115 // Some languages won't have a notion of manipulating collections. Exposing "length" on arrays as an 116 // explicit property is beneficial for them. 117 setPropertyGetter("length", MethodHandles.arrayLength(clazz), ValidationType.EXACT_CLASS); 118 } else if(Collection.class.isAssignableFrom(clazz)) { 119 setPropertyGetter("length", GET_COLLECTION_LENGTH, ValidationType.INSTANCE_OF); 120 } else if(Map.class.isAssignableFrom(clazz)) { 121 setPropertyGetter("length", GET_MAP_LENGTH, ValidationType.INSTANCE_OF); 122 } 123 } 124 125 @Override 126 public boolean canLinkType(final Class<?> type) { 127 return type == clazz; 128 } 129 130 @Override 131 FacetIntrospector createFacetIntrospector() { 132 return new BeanIntrospector(clazz); 133 } 134 135 @Override 136 protected GuardedInvocationComponent getGuardedInvocationComponent(final ComponentLinkRequest req) throws Exception { 137 final GuardedInvocationComponent superGic = super.getGuardedInvocationComponent(req); 138 if(superGic != null) { 139 return superGic; 140 } 141 if (!req.namespaces.isEmpty()) { 142 final Operation op = req.baseOperation; 143 final Namespace ns = req.namespaces.get(0); 144 if (ns == StandardNamespace.ELEMENT) { 145 if (op == StandardOperation.GET) { 146 return getElementGetter(req.popNamespace()); 147 } else if (op == StandardOperation.SET) { 148 return getElementSetter(req.popNamespace()); 149 } 150 } 151 } 152 return null; 153 } 154 155 @Override 156 SingleDynamicMethod getConstructorMethod(final String signature) { 157 return null; 158 } 159 160 private static final MethodHandle GET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "get", 161 MethodType.methodType(Object.class, int.class)); 162 163 private static final MethodHandle GET_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "get", 164 MethodType.methodType(Object.class, Object.class)); 165 166 private static final MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class); 167 private static final MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class); 168 169 private static final MethodHandle NULL_GETTER_1; 170 private static final MethodHandle NULL_GETTER_2; 171 static { 172 final MethodHandle constantNull = MethodHandles.constant(Object.class, null); 173 NULL_GETTER_1 = dropObjectArguments(constantNull, 1); 174 NULL_GETTER_2 = dropObjectArguments(constantNull, 2); 175 } 176 177 private static MethodHandle dropObjectArguments(final MethodHandle m, final int n) { 178 return MethodHandles.dropArguments(m, 0, Collections.nCopies(n, Object.class)); 179 } 180 181 private enum CollectionType { 182 ARRAY, LIST, MAP 183 }; 184 185 private GuardedInvocationComponent getElementGetter(final ComponentLinkRequest req) throws Exception { 186 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); 187 final Object name = req.name; 188 final boolean isFixedKey = name != null; 189 assertParameterCount(callSiteDescriptor, isFixedKey ? 1 : 2); 190 final LinkerServices linkerServices = req.linkerServices; 191 final MethodType callSiteType = callSiteDescriptor.getMethodType(); 192 final Class<?> declaredType = callSiteType.parameterType(0); 193 final GuardedInvocationComponent nextComponent = getNextComponent(req); 194 195 // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing 196 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're 197 // dealing with an array, or a list or map, but hey... 198 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers 199 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. 200 final GuardedInvocationComponent gic; 201 final CollectionType collectionType; 202 if(declaredType.isArray()) { 203 gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType), linkerServices); 204 collectionType = CollectionType.ARRAY; 205 } else if(List.class.isAssignableFrom(declaredType)) { 206 gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, linkerServices); 207 collectionType = CollectionType.LIST; 208 } else if(Map.class.isAssignableFrom(declaredType)) { 209 gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, linkerServices); 210 collectionType = CollectionType.MAP; 211 } else if(clazz.isArray()) { 212 gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(MethodHandles.arrayElementGetter(clazz)), callSiteType); 213 collectionType = CollectionType.ARRAY; 214 } else if(List.class.isAssignableFrom(clazz)) { 215 gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF, 216 linkerServices); 217 collectionType = CollectionType.LIST; 218 } else if(Map.class.isAssignableFrom(clazz)) { 219 gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, ValidationType.INSTANCE_OF, 220 linkerServices); 221 collectionType = CollectionType.MAP; 222 } else { 223 // Can't retrieve elements for objects that are neither arrays, nor list, nor maps. 224 return nextComponent; 225 } 226 227 // Convert the key to a number if we're working with a list or array 228 final Object typedName; 229 if (collectionType != CollectionType.MAP && isFixedKey) { 230 final Integer integer = convertKeyToInteger(name, linkerServices); 231 if (integer == null || integer.intValue() < 0) { 232 // key is not a non-negative integer, it can never address an 233 // array or list element 234 return nextComponent; 235 } 236 typedName = integer; 237 } else { 238 typedName = name; 239 } 240 241 final GuardedInvocation gi = gic.getGuardedInvocation(); 242 final Binder binder = new Binder(linkerServices, callSiteType, typedName); 243 final MethodHandle invocation = gi.getInvocation(); 244 245 final MethodHandle checkGuard; 246 switch(collectionType) { 247 case LIST: 248 checkGuard = convertArgToNumber(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor); 249 break; 250 case MAP: 251 checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP); 252 break; 253 case ARRAY: 254 checkGuard = convertArgToNumber(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); 255 break; 256 default: 257 throw new AssertionError(); 258 } 259 260 // If there's no next component, produce a fixed null-returning one 261 final GuardedInvocationComponent finalNextComponent; 262 if (nextComponent != null) { 263 finalNextComponent = nextComponent; 264 } else { 265 final MethodHandle nullGetterHandle = isFixedKey ? NULL_GETTER_1 : NULL_GETTER_2; 266 finalNextComponent = createGuardedInvocationComponentAsType(nullGetterHandle, callSiteType, linkerServices); 267 } 268 269 final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation), 270 finalNextComponent.getGuardedInvocation().getInvocation()); 271 return finalNextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(), 272 gic.getValidatorClass(), gic.getValidationType()); 273 } 274 275 private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent( 276 final MethodHandle invocation, final LinkerServices linkerServices) { 277 return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation)); 278 } 279 280 private static GuardedInvocationComponent createGuardedInvocationComponentAsType( 281 final MethodHandle invocation, final MethodType fromType, final LinkerServices linkerServices) { 282 return new GuardedInvocationComponent(linkerServices.asType(invocation, fromType)); 283 } 284 285 private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent( 286 final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass, 287 final ValidationType validationType, final LinkerServices linkerServices) { 288 return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation), guard, 289 validatorClass, validationType); 290 } 291 292 private static Integer convertKeyToInteger(final Object fixedKey, final LinkerServices linkerServices) throws Exception { 293 if (fixedKey instanceof Integer) { 294 return (Integer)fixedKey; 295 } 296 297 final Number n; 298 if (fixedKey instanceof Number) { 299 n = (Number)fixedKey; 300 } else { 301 final Class<?> keyClass = fixedKey.getClass(); 302 if(linkerServices.canConvert(keyClass, Number.class)) { 303 final Object val; 304 try { 305 val = linkerServices.getTypeConverter(keyClass, Number.class).invoke(fixedKey); 306 } catch(Exception|Error e) { 307 throw e; 308 } catch(final Throwable t) { 309 throw new RuntimeException(t); 310 } 311 if(!(val instanceof Number)) { 312 return null; // not a number 313 } 314 n = (Number)val; 315 } else if (fixedKey instanceof String){ 316 try { 317 return Integer.valueOf((String)fixedKey); 318 } catch(final NumberFormatException e) { 319 // key is not a number 320 return null; 321 } 322 } else { 323 return null; 324 } 325 } 326 327 if(n instanceof Integer) { 328 return (Integer)n; 329 } 330 final int intIndex = n.intValue(); 331 final double doubleValue = n.doubleValue(); 332 if(intIndex != doubleValue && !Double.isInfinite(doubleValue)) { // let infinites trigger IOOBE 333 return null; // not an exact integer 334 } 335 return intIndex; 336 } 337 338 private static MethodHandle convertArgToNumber(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) { 339 final Class<?> sourceType = desc.getMethodType().parameterType(1); 340 if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) { 341 return mh; 342 } else if(ls.canConvert(sourceType, Number.class)) { 343 final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class); 344 return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType( 345 mh.type().parameterType(1)))); 346 } 347 return mh; 348 } 349 350 /** 351 * Contains methods to adapt an item getter/setter method handle to the requested type, optionally binding it to a 352 * fixed key first. 353 */ 354 private static class Binder { 355 private final LinkerServices linkerServices; 356 private final MethodType methodType; 357 private final Object fixedKey; 358 359 Binder(final LinkerServices linkerServices, final MethodType methodType, final Object fixedKey) { 360 this.linkerServices = linkerServices; 361 this.methodType = fixedKey == null ? methodType : methodType.insertParameterTypes(1, fixedKey.getClass()); 362 this.fixedKey = fixedKey; 363 } 364 365 /*private*/ MethodHandle bind(final MethodHandle handle) { 366 return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType)); 367 } 368 369 /*private*/ MethodHandle bindTest(final MethodHandle handle) { 370 return bindToFixedKey(Guards.asType(handle, methodType)); 371 } 372 373 private MethodHandle bindToFixedKey(final MethodHandle handle) { 374 return fixedKey == null ? handle : MethodHandles.insertArguments(handle, 1, fixedKey); 375 } 376 } 377 378 private static final MethodHandle RANGE_CHECK_ARRAY = findRangeCheck(Object.class); 379 private static final MethodHandle RANGE_CHECK_LIST = findRangeCheck(List.class); 380 private static final MethodHandle CONTAINS_MAP = Lookup.PUBLIC.findVirtual(Map.class, "containsKey", 381 MethodType.methodType(boolean.class, Object.class)); 382 383 private static MethodHandle findRangeCheck(final Class<?> collectionType) { 384 return Lookup.findOwnStatic(MethodHandles.lookup(), "rangeCheck", boolean.class, collectionType, Object.class); 385 } 386 387 @SuppressWarnings("unused") 388 private static boolean rangeCheck(final Object array, final Object index) { 389 if(!(index instanceof Number)) { 390 return false; 391 } 392 final Number n = (Number)index; 393 final int intIndex = n.intValue(); 394 if (intIndex != n.doubleValue()) { 395 return false; 396 } 397 return 0 <= intIndex && intIndex < Array.getLength(array); 398 } 399 400 @SuppressWarnings("unused") 401 private static boolean rangeCheck(final List<?> list, final Object index) { 402 if(!(index instanceof Number)) { 403 return false; 404 } 405 final Number n = (Number)index; 406 final int intIndex = n.intValue(); 407 if (intIndex != n.doubleValue()) { 408 return false; 409 } 410 return 0 <= intIndex && intIndex < list.size(); 411 } 412 413 @SuppressWarnings("unused") 414 private static void noOpSetter() { 415 } 416 417 private static final MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set", 418 MethodType.methodType(Object.class, int.class, Object.class)); 419 420 private static final MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put", 421 MethodType.methodType(Object.class, Object.class, Object.class)); 422 423 private static final MethodHandle NO_OP_SETTER_2; 424 private static final MethodHandle NO_OP_SETTER_3; 425 static { 426 final MethodHandle noOpSetter = Lookup.findOwnStatic(MethodHandles.lookup(), "noOpSetter", void.class); 427 NO_OP_SETTER_2 = dropObjectArguments(noOpSetter, 2); 428 NO_OP_SETTER_3 = dropObjectArguments(noOpSetter, 3); 429 } 430 431 private GuardedInvocationComponent getElementSetter(final ComponentLinkRequest req) throws Exception { 432 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); 433 final Object name = req.name; 434 final boolean isFixedKey = name != null; 435 assertParameterCount(callSiteDescriptor, isFixedKey ? 2 : 3); 436 final LinkerServices linkerServices = req.linkerServices; 437 final MethodType callSiteType = callSiteDescriptor.getMethodType(); 438 final Class<?> declaredType = callSiteType.parameterType(0); 439 440 final GuardedInvocationComponent gic; 441 // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing 442 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're 443 // dealing with an array, or a list or map, but hey... 444 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers 445 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. 446 final CollectionType collectionType; 447 if(declaredType.isArray()) { 448 gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType), linkerServices); 449 collectionType = CollectionType.ARRAY; 450 } else if(List.class.isAssignableFrom(declaredType)) { 451 gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, linkerServices); 452 collectionType = CollectionType.LIST; 453 } else if(Map.class.isAssignableFrom(declaredType)) { 454 gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, linkerServices); 455 collectionType = CollectionType.MAP; 456 } else if(clazz.isArray()) { 457 gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects( 458 MethodHandles.arrayElementSetter(clazz)), callSiteType); 459 collectionType = CollectionType.ARRAY; 460 } else if(List.class.isAssignableFrom(clazz)) { 461 gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF, 462 linkerServices); 463 collectionType = CollectionType.LIST; 464 } else if(Map.class.isAssignableFrom(clazz)) { 465 gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), 466 Map.class, ValidationType.INSTANCE_OF, linkerServices); 467 collectionType = CollectionType.MAP; 468 } else { 469 // Can't set elements for objects that are neither arrays, nor list, nor maps. 470 gic = null; 471 collectionType = null; 472 } 473 474 // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map, 475 // as maps will always succeed in setting the element and will never need to fall back to the next component 476 // operation. 477 final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getNextComponent(req); 478 if(gic == null) { 479 return nextComponent; 480 } 481 482 // Convert the key to a number if we're working with a list or array 483 final Object typedName; 484 if (collectionType != CollectionType.MAP && isFixedKey) { 485 final Integer integer = convertKeyToInteger(name, linkerServices); 486 if (integer == null || integer.intValue() < 0) { 487 // key is not a non-negative integer, it can never address an 488 // array or list element 489 return nextComponent; 490 } 491 typedName = integer; 492 } else { 493 typedName = name; 494 } 495 496 final GuardedInvocation gi = gic.getGuardedInvocation(); 497 final Binder binder = new Binder(linkerServices, callSiteType, typedName); 498 final MethodHandle invocation = gi.getInvocation(); 499 500 if (collectionType == CollectionType.MAP) { 501 assert nextComponent == null; 502 return gic.replaceInvocation(binder.bind(invocation)); 503 } 504 505 assert collectionType == CollectionType.LIST || collectionType == CollectionType.ARRAY; 506 final MethodHandle checkGuard = convertArgToNumber(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST : 507 RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); 508 509 // If there's no next component, produce a no-op one. 510 final GuardedInvocationComponent finalNextComponent; 511 if (nextComponent != null) { 512 finalNextComponent = nextComponent; 513 } else { 514 final MethodHandle noOpSetterHandle = isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3; 515 finalNextComponent = createGuardedInvocationComponentAsType(noOpSetterHandle, callSiteType, linkerServices); 516 } 517 518 final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation), 519 finalNextComponent.getGuardedInvocation().getInvocation()); 520 return finalNextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(), 521 gic.getValidatorClass(), gic.getValidationType()); 522 } 523 524 private static final MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size", 525 MethodType.methodType(int.class)); 526 527 private static final MethodHandle GET_MAP_LENGTH = Lookup.PUBLIC.findVirtual(Map.class, "size", 528 MethodType.methodType(int.class)); 529 530 private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) { 531 if(descriptor.getMethodType().parameterCount() != paramCount) { 532 throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters."); 533 } 534 } 535} 536