src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java
changeset 48244 2bf9071e8dce
parent 48219 f3b561b13ddf
child 48354 c96d4c720995
--- a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java	Tue Dec 12 14:04:05 2017 +0100
+++ b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java	Tue Dec 12 14:04:57 2017 +0100
@@ -107,7 +107,8 @@
 
 /**
  * A class that provides linking capabilities for a single POJO class. Normally not used directly, but managed by
- * {@link BeansLinker}.
+ * {@link BeansLinker}. Most of the functionality is provided by the {@link AbstractJavaLinker} superclass; this
+ * class adds length and element operations for arrays and collections.
  */
 class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker {
     BeanLinker(final Class<?> clazz) {
@@ -147,6 +148,8 @@
                     return getElementGetter(req.popNamespace());
                 } else if (op == StandardOperation.SET) {
                     return getElementSetter(req.popNamespace());
+                } else if (op == StandardOperation.REMOVE) {
+                    return getElementRemover(req.popNamespace());
                 }
             }
         }
@@ -228,7 +231,7 @@
         // dealing with an array, or a list or map, but hey...
         // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
         // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
-        if(declaredType.isArray()) {
+        if(declaredType.isArray() && arrayMethod != null) {
             return new GuardedInvocationComponentAndCollectionType(
                     createInternalFilteredGuardedInvocationComponent(arrayMethod.apply(declaredType), linkerServices),
                     CollectionType.ARRAY);
@@ -240,7 +243,7 @@
             return new GuardedInvocationComponentAndCollectionType(
                     createInternalFilteredGuardedInvocationComponent(mapMethod, linkerServices),
                     CollectionType.MAP);
-        } else if(clazz.isArray()) {
+        } else if(clazz.isArray() && arrayMethod != null) {
             return new GuardedInvocationComponentAndCollectionType(
                     getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(arrayMethod.apply(clazz)), callSiteType),
                     CollectionType.ARRAY);
@@ -450,7 +453,7 @@
     }
 
     @SuppressWarnings("unused")
-    private static void noOpSetter() {
+    private static void noOp() {
     }
 
     private static final MethodHandle SET_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "set",
@@ -459,12 +462,14 @@
     private static final MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put",
             MethodType.methodType(Object.class, Object.class, Object.class));
 
-    private static final MethodHandle NO_OP_SETTER_2;
-    private static final MethodHandle NO_OP_SETTER_3;
+    private static final MethodHandle NO_OP_1;
+    private static final MethodHandle NO_OP_2;
+    private static final MethodHandle NO_OP_3;
     static {
-        final MethodHandle noOpSetter = Lookup.findOwnStatic(MethodHandles.lookup(), "noOpSetter", void.class);
-        NO_OP_SETTER_2 = dropObjectArguments(noOpSetter, 2);
-        NO_OP_SETTER_3 = dropObjectArguments(noOpSetter, 3);
+        final MethodHandle noOp = Lookup.findOwnStatic(MethodHandles.lookup(), "noOp", void.class);
+        NO_OP_1 = dropObjectArguments(noOp, 1);
+        NO_OP_2 = dropObjectArguments(noOp, 2);
+        NO_OP_3 = dropObjectArguments(noOp, 3);
     }
 
     private GuardedInvocationComponent getElementSetter(final ComponentLinkRequest req) throws Exception {
@@ -503,7 +508,39 @@
             return gic.replaceInvocation(binder.bind(invocation));
         }
 
-        return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, binder, isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3);
+        return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, binder, isFixedKey ? NO_OP_2 : NO_OP_3);
+    }
+
+    private static final MethodHandle REMOVE_LIST_ELEMENT = Lookup.PUBLIC.findVirtual(List.class, "remove",
+            MethodType.methodType(Object.class, int.class));
+
+    private static final MethodHandle REMOVE_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "remove",
+            MethodType.methodType(Object.class, Object.class));
+
+    private GuardedInvocationComponent getElementRemover(final ComponentLinkRequest req) throws Exception {
+        final CallSiteDescriptor callSiteDescriptor = req.getDescriptor();
+        final Object name = req.name;
+        final boolean isFixedKey = name != null;
+        assertParameterCount(callSiteDescriptor, isFixedKey ? 1 : 2);
+        final LinkerServices linkerServices = req.linkerServices;
+        final MethodType callSiteType = callSiteDescriptor.getMethodType();
+        final GuardedInvocationComponent nextComponent = getNextComponent(req);
+
+        final GuardedInvocationComponentAndCollectionType gicact = guardedInvocationComponentAndCollectionType(
+                callSiteType, linkerServices, null, REMOVE_LIST_ELEMENT, REMOVE_MAP_ELEMENT);
+
+        if (gicact == null) {
+            // Can't remove elements for objects that are neither lists, nor maps.
+            return nextComponent;
+        }
+
+        final Object typedName = getTypedName(name, gicact.collectionType == CollectionType.MAP, linkerServices);
+        if (typedName == INVALID_NAME) {
+            return nextComponent;
+        }
+
+        return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent,
+                new Binder(linkerServices, callSiteType, typedName), isFixedKey ? NO_OP_1: NO_OP_2);
     }
 
     private static final MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size",