105 import jdk.dynalink.linker.support.Lookup; |
105 import jdk.dynalink.linker.support.Lookup; |
106 import jdk.dynalink.linker.support.TypeUtilities; |
106 import jdk.dynalink.linker.support.TypeUtilities; |
107 |
107 |
108 /** |
108 /** |
109 * A class that provides linking capabilities for a single POJO class. Normally not used directly, but managed by |
109 * A class that provides linking capabilities for a single POJO class. Normally not used directly, but managed by |
110 * {@link BeansLinker}. |
110 * {@link BeansLinker}. Most of the functionality is provided by the {@link AbstractJavaLinker} superclass; this |
|
111 * class adds length and element operations for arrays and collections. |
111 */ |
112 */ |
112 class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker { |
113 class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker { |
113 BeanLinker(final Class<?> clazz) { |
114 BeanLinker(final Class<?> clazz) { |
114 super(clazz, Guards.getClassGuard(clazz), Guards.getInstanceOfGuard(clazz)); |
115 super(clazz, Guards.getClassGuard(clazz), Guards.getInstanceOfGuard(clazz)); |
115 if(clazz.isArray()) { |
116 if(clazz.isArray()) { |
145 if (ns == StandardNamespace.ELEMENT) { |
146 if (ns == StandardNamespace.ELEMENT) { |
146 if (op == StandardOperation.GET) { |
147 if (op == StandardOperation.GET) { |
147 return getElementGetter(req.popNamespace()); |
148 return getElementGetter(req.popNamespace()); |
148 } else if (op == StandardOperation.SET) { |
149 } else if (op == StandardOperation.SET) { |
149 return getElementSetter(req.popNamespace()); |
150 return getElementSetter(req.popNamespace()); |
|
151 } else if (op == StandardOperation.REMOVE) { |
|
152 return getElementRemover(req.popNamespace()); |
150 } |
153 } |
151 } |
154 } |
152 } |
155 } |
153 return null; |
156 return null; |
154 } |
157 } |
226 // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing |
229 // If declared type of receiver at the call site is already an array, a list or map, bind without guard. Thing |
227 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're |
230 // is, it'd be quite stupid of a call site creator to go though invokedynamic when it knows in advance they're |
228 // dealing with an array, or a list or map, but hey... |
231 // dealing with an array, or a list or map, but hey... |
229 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers |
232 // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers |
230 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. |
233 // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. |
231 if(declaredType.isArray()) { |
234 if(declaredType.isArray() && arrayMethod != null) { |
232 return new GuardedInvocationComponentAndCollectionType( |
235 return new GuardedInvocationComponentAndCollectionType( |
233 createInternalFilteredGuardedInvocationComponent(arrayMethod.apply(declaredType), linkerServices), |
236 createInternalFilteredGuardedInvocationComponent(arrayMethod.apply(declaredType), linkerServices), |
234 CollectionType.ARRAY); |
237 CollectionType.ARRAY); |
235 } else if(List.class.isAssignableFrom(declaredType)) { |
238 } else if(List.class.isAssignableFrom(declaredType)) { |
236 return new GuardedInvocationComponentAndCollectionType( |
239 return new GuardedInvocationComponentAndCollectionType( |
238 CollectionType.LIST); |
241 CollectionType.LIST); |
239 } else if(Map.class.isAssignableFrom(declaredType)) { |
242 } else if(Map.class.isAssignableFrom(declaredType)) { |
240 return new GuardedInvocationComponentAndCollectionType( |
243 return new GuardedInvocationComponentAndCollectionType( |
241 createInternalFilteredGuardedInvocationComponent(mapMethod, linkerServices), |
244 createInternalFilteredGuardedInvocationComponent(mapMethod, linkerServices), |
242 CollectionType.MAP); |
245 CollectionType.MAP); |
243 } else if(clazz.isArray()) { |
246 } else if(clazz.isArray() && arrayMethod != null) { |
244 return new GuardedInvocationComponentAndCollectionType( |
247 return new GuardedInvocationComponentAndCollectionType( |
245 getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(arrayMethod.apply(clazz)), callSiteType), |
248 getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(arrayMethod.apply(clazz)), callSiteType), |
246 CollectionType.ARRAY); |
249 CollectionType.ARRAY); |
247 } else if(List.class.isAssignableFrom(clazz)) { |
250 } else if(List.class.isAssignableFrom(clazz)) { |
248 return new GuardedInvocationComponentAndCollectionType( |
251 return new GuardedInvocationComponentAndCollectionType( |
448 } |
451 } |
449 return 0 <= intIndex && intIndex < list.size(); |
452 return 0 <= intIndex && intIndex < list.size(); |
450 } |
453 } |
451 |
454 |
452 @SuppressWarnings("unused") |
455 @SuppressWarnings("unused") |
453 private static void noOpSetter() { |
456 private static void noOp() { |
454 } |
457 } |
455 |
458 |
456 private static final MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set", |
459 private static final MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set", |
457 MethodType.methodType(Object.class, int.class, Object.class)); |
460 MethodType.methodType(Object.class, int.class, Object.class)); |
458 |
461 |
459 private static final MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put", |
462 private static final MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put", |
460 MethodType.methodType(Object.class, Object.class, Object.class)); |
463 MethodType.methodType(Object.class, Object.class, Object.class)); |
461 |
464 |
462 private static final MethodHandle NO_OP_SETTER_2; |
465 private static final MethodHandle NO_OP_1; |
463 private static final MethodHandle NO_OP_SETTER_3; |
466 private static final MethodHandle NO_OP_2; |
|
467 private static final MethodHandle NO_OP_3; |
464 static { |
468 static { |
465 final MethodHandle noOpSetter = Lookup.findOwnStatic(MethodHandles.lookup(), "noOpSetter", void.class); |
469 final MethodHandle noOp = Lookup.findOwnStatic(MethodHandles.lookup(), "noOp", void.class); |
466 NO_OP_SETTER_2 = dropObjectArguments(noOpSetter, 2); |
470 NO_OP_1 = dropObjectArguments(noOp, 1); |
467 NO_OP_SETTER_3 = dropObjectArguments(noOpSetter, 3); |
471 NO_OP_2 = dropObjectArguments(noOp, 2); |
|
472 NO_OP_3 = dropObjectArguments(noOp, 3); |
468 } |
473 } |
469 |
474 |
470 private GuardedInvocationComponent getElementSetter(final ComponentLinkRequest req) throws Exception { |
475 private GuardedInvocationComponent getElementSetter(final ComponentLinkRequest req) throws Exception { |
471 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); |
476 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); |
472 final Object name = req.name; |
477 final Object name = req.name; |
501 |
506 |
502 if (isMap) { |
507 if (isMap) { |
503 return gic.replaceInvocation(binder.bind(invocation)); |
508 return gic.replaceInvocation(binder.bind(invocation)); |
504 } |
509 } |
505 |
510 |
506 return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, binder, isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3); |
511 return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, binder, isFixedKey ? NO_OP_2 : NO_OP_3); |
|
512 } |
|
513 |
|
514 private static final MethodHandle REMOVE_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "remove", |
|
515 MethodType.methodType(Object.class, int.class)); |
|
516 |
|
517 private static final MethodHandle REMOVE_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "remove", |
|
518 MethodType.methodType(Object.class, Object.class)); |
|
519 |
|
520 private GuardedInvocationComponent getElementRemover(final ComponentLinkRequest req) throws Exception { |
|
521 final CallSiteDescriptor callSiteDescriptor = req.getDescriptor(); |
|
522 final Object name = req.name; |
|
523 final boolean isFixedKey = name != null; |
|
524 assertParameterCount(callSiteDescriptor, isFixedKey ? 1 : 2); |
|
525 final LinkerServices linkerServices = req.linkerServices; |
|
526 final MethodType callSiteType = callSiteDescriptor.getMethodType(); |
|
527 final GuardedInvocationComponent nextComponent = getNextComponent(req); |
|
528 |
|
529 final GuardedInvocationComponentAndCollectionType gicact = guardedInvocationComponentAndCollectionType( |
|
530 callSiteType, linkerServices, null, REMOVE_LIST_ELEMENT, REMOVE_MAP_ELEMENT); |
|
531 |
|
532 if (gicact == null) { |
|
533 // Can't remove elements for objects that are neither lists, nor maps. |
|
534 return nextComponent; |
|
535 } |
|
536 |
|
537 final Object typedName = getTypedName(name, gicact.collectionType == CollectionType.MAP, linkerServices); |
|
538 if (typedName == INVALID_NAME) { |
|
539 return nextComponent; |
|
540 } |
|
541 |
|
542 return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, |
|
543 new Binder(linkerServices, callSiteType, typedName), isFixedKey ? NO_OP_1: NO_OP_2); |
507 } |
544 } |
508 |
545 |
509 private static final MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size", |
546 private static final MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size", |
510 MethodType.methodType(int.class)); |
547 MethodType.methodType(int.class)); |
511 |
548 |