nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java
changeset 20945 11a1b6dc9893
parent 18841 9bbc4b8832b2
child 24719 f726e9d67629
--- a/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Mon Oct 14 12:41:11 2013 +0200
+++ b/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java	Tue Oct 15 15:57:14 2013 +0200
@@ -91,6 +91,7 @@
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.support.Guards;
+import jdk.internal.dynalink.support.Lookup;
 
 /**
  * Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the
@@ -100,6 +101,9 @@
  * @version $Id: $
  */
 abstract class SingleDynamicMethod extends DynamicMethod {
+
+    private static final MethodHandle CAN_CONVERT_TO = Lookup.findOwnStatic(MethodHandles.lookup(), "canConvertTo", boolean.class, LinkerServices.class, Class.class, Object.class);
+
     SingleDynamicMethod(String name) {
         super(name);
     }
@@ -201,23 +205,69 @@
                 return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector(
                         callSiteLastArgType);
             }
-            if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) {
-                // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive);
-                // link immediately to a vararg-packing method handle.
-                return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
+
+            // This method handle takes the single argument and packs it into a newly allocated single-element array. It
+            // will be used when the incoming argument can't be converted to the vararg array type (the "vararg packer"
+            // method).
+            final MethodHandle varArgCollectingInvocation = createConvertingInvocation(collectArguments(fixTarget,
+                    argsLen), linkerServices, callSiteType);
+
+            // Is call site type assignable from an array type (e.g. Object:int[], or Object[]:String[])
+            final boolean isAssignableFromArray = callSiteLastArgType.isAssignableFrom(varArgType);
+            // Do we have a custom conversion that can potentially convert the call site type to an array?
+            final boolean isCustomConvertible = linkerServices.canConvert(callSiteLastArgType, varArgType);
+            if(!isAssignableFromArray && !isCustomConvertible) {
+                // Call site signature guarantees the argument can definitely not be converted to an array (i.e. it is
+                // primitive), and no conversion can help with it either. Link immediately to a vararg-packing method
+                // handle.
+                return varArgCollectingInvocation;
             }
-            // Call site signature makes no guarantees that the single argument in the vararg position will be
-            // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg
-            // method when it is not.
-            return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType),
-                    createConvertingInvocation(fixTarget, linkerServices, callSiteType),
-                    createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType));
+
+            // This method handle employs language-specific conversions to convert the last argument into an array of
+            // vararg type.
+            final MethodHandle arrayConvertingInvocation = createConvertingInvocation(MethodHandles.filterArguments(
+                    fixTarget, fixParamsLen, linkerServices.getTypeConverter(callSiteLastArgType, varArgType)),
+                    linkerServices, callSiteType);
+
+            // This method handle determines whether the value can be converted to the array of vararg type using a
+            // language-specific conversion.
+            final MethodHandle canConvertArgToArray = MethodHandles.insertArguments(CAN_CONVERT_TO, 0, linkerServices,
+                    varArgType);
+
+            // This one adjusts the previous one for the location of the argument and the call site type.
+            final MethodHandle canConvertLastArgToArray = MethodHandles.dropArguments(canConvertArgToArray, 0,
+                    MethodType.genericMethodType(fixParamsLen).parameterList()).asType(callSiteType.changeReturnType(boolean.class));
+
+            // This one takes the previous ones and combines them into a method handle that converts the argument into
+            // a vararg array when it can, otherwise falls back to the vararg packer.
+            final MethodHandle convertToArrayWhenPossible = MethodHandles.guardWithTest(canConvertLastArgToArray,
+                    arrayConvertingInvocation, varArgCollectingInvocation);
+
+            if(isAssignableFromArray) {
+                return MethodHandles.guardWithTest(
+                        // Is incoming parameter already a compatible array?
+                        Guards.isInstance(varArgType, fixParamsLen, callSiteType),
+                        // Yes: just pass it to the method
+                        createConvertingInvocation(fixTarget, linkerServices, callSiteType),
+                        // No: either go through a custom conversion, or if it is not possible, go directly to the
+                        // vararg packer.
+                        isCustomConvertible ? convertToArrayWhenPossible : varArgCollectingInvocation);
+            }
+
+            // Just do the custom conversion with fallback to the vararg packer logic.
+            assert isCustomConvertible;
+            return convertToArrayWhenPossible;
         }
 
         // Remaining case: more than one vararg.
         return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
     }
 
+    @SuppressWarnings("unused")
+    private static boolean canConvertTo(final LinkerServices linkerServices, Class<?> to, Object obj) {
+        return obj == null ? false : linkerServices.canConvert(obj.getClass(), to);
+    }
+
     /**
      * Creates a method handle out of the original target that will collect the varargs for the exact component type of
      * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs