Attached is my working patch. I've signed the developer agreement and sent it to the Oracle as required.

I've marked all my changes with a "// TAL" comment so you can easily find them.

Here is the explanation:

JavaArgumentConverters.java: I've added new converters for all primitive array types, as well as Object[] and String[]. For NativeArrays, it would convert all the individual elements. For other object types, it would attempt to create a one-element array, assuming that element is convertible. I added a few utility methods, too, to help with this. I also took the liberty to make a change to toString: it will fallback to Object.toString. (I have a feeling some of you will not like this change, as it's not directly related to the feature, but I see no harm in it, and it much helped my testing. In fact, it's even Java's behavior when working with non-string objects. Without this change, JavaScript programmers would still have to work hard to set up arrays before sending them to Java.)

NashornPrimitiveLinker.java: canLinkTypeStatic now recognizes all the supported primitive array types. In compareConversion, I've also added a check to prefer conversion to arrays over conversion to primitives. Without this, an ambiguous exception could be thrown in cases where two similar method signatures exist. (For example, java.lang.Runtime.execute has a version that accepts a string array and another that accepts a string.) I think this can be further improved to prefer Object[] over String[] if there is such ambiguity. What I need help with from you guys is in getGuardedInvocation: I currently return null for NativeArray, because I'm not sure how to get an appropriate guard. Currently primitiveLookup only supports non-array primitive types. I think what happens in this case is that a default guard is provided, but I'm not entirely sure.

Guards.java: I've added extra checks to avoid the isInstanceGuardAlwaysFalse log warning for array conversions.


On 10/09/2013 10:28 PM, Jim Laskey wrote:
If you can get the changes to me by Tuesday Oct 15th, I'll take a look.  No 
guarantees, but if the changes are small, are correct, and do not restrict 
future enhancements in this area then I'll run the changes up the approval 
chain.



diff --git a/src/jdk/internal/dynalink/support/Guards.java 
b/src/jdk/internal/dynalink/support/Guards.java
--- a/src/jdk/internal/dynalink/support/Guards.java
+++ b/src/jdk/internal/dynalink/support/Guards.java
@@ -155,7 +155,18 @@
             LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { 
clazz.getName(), pos, type });
             return constantTrue(type);
         }
-        if(!declaredType.isAssignableFrom(clazz)) {
+        if(!declaredType.isAssignableFrom(clazz) &&
+               // TAL
+               clazz != Object[].class &&
+               clazz != String[].class &&
+               clazz != boolean[].class &&
+               clazz != float[].class &&
+                       clazz != double[].class &&
+                       clazz != byte[].class &&
+                       clazz != short[].class &&
+                       clazz != int[].class &&
+                       clazz != long[].class &&
+                       clazz != char[].class) {
             LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] 
{ clazz.getName(), pos, type });
             return constantFalse(type);
         }
diff --git 
a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java 
b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java
--- a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java
@@ -25,18 +25,21 @@
 
 package jdk.nashorn.internal.runtime.linker;
 
+import static jdk.nashorn.internal.lookup.Lookup.MH;
 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.util.HashMap;
 import java.util.Map;
+
 import jdk.internal.dynalink.support.TypeUtilities;
+import jdk.nashorn.internal.objects.NativeArray;
 import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.arrays.ArrayData;
 
 /**
  * Utility class shared by {@code NashornLinker} and {@code 
NashornPrimitiveLinker} for converting JS values to Java
@@ -53,6 +56,18 @@
     private static final MethodHandle TO_CHAR           = findOwnMH("toChar", 
Character.class, Object.class);
     private static final MethodHandle TO_CHAR_PRIMITIVE = 
findOwnMH("toCharPrimitive", char.class, Object.class);
 
+    // TAL
+    private static final MethodHandle TO_OBJECT_ARRAY            = 
findOwnMH("toObjectArray", Object[].class, Object.class);
+    private static final MethodHandle TO_STRING_ARRAY            = 
findOwnMH("toStringArray", String[].class, Object.class);
+    private static final MethodHandle TO_BOOLEAN_PRIMITIVE_ARRAY = 
findOwnMH("toBooleanPrimitiveArray", boolean[].class, Object.class);
+    private static final MethodHandle TO_FLOAT_PRIMITIVE_ARRAY   = 
findOwnMH("toFloatPrimitiveArray", float[].class, Object.class);
+    private static final MethodHandle TO_DOUBLE_PRIMITIVE_ARRAY  = 
findOwnMH("toDoublePrimitiveArray", double[].class, Object.class);
+    private static final MethodHandle TO_BYTE_PRIMITIVE_ARRAY    = 
findOwnMH("toBytePrimitiveArray", byte[].class, Object.class);
+    private static final MethodHandle TO_SHORT_PRIMITIVE_ARRAY   = 
findOwnMH("toShortPrimitiveArray", short[].class, Object.class);
+    private static final MethodHandle TO_INT_PRIMITIVE_ARRAY     = 
findOwnMH("toIntPrimitiveArray", int[].class, Object.class);
+    private static final MethodHandle TO_LONG_PRIMITIVE_ARRAY    = 
findOwnMH("toLongPrimitiveArray", long[].class, Object.class);
+    private static final MethodHandle TO_CHAR_PRIMITIVE_ARRAY    = 
findOwnMH("toCharPrimitiveArray", char[].class, Object.class);
+
     private JavaArgumentConverters() {
     }
 
@@ -60,7 +75,6 @@
         return CONVERTERS.get(targetType);
     }
 
-    @SuppressWarnings("unused")
     private static Boolean toBoolean(final Object obj) {
         if (obj instanceof Boolean) {
             return (Boolean) obj;
@@ -98,6 +112,12 @@
         throw assertUnexpectedType(obj);
     }
 
+    // TAL
+    private static boolean toBooleanPrimitive(final Object obj0) {
+        final Boolean b = toBoolean(obj0);
+        return b == null ? false : b;
+    }
+
     private static Character toChar(final Object o) {
         if (o == null) {
             return null;
@@ -124,7 +144,6 @@
         return s.charAt(0);
     }
 
-    @SuppressWarnings("unused")
     private static char toCharPrimitive(final Object obj0) {
         final Character c = toChar(obj0);
         return c == null ? (char)0 : c;
@@ -150,11 +169,12 @@
                 obj = JSType.toPrimitive(obj, String.class);
                 continue;
             }
-            throw assertUnexpectedType(obj);
+            // TAL
+            return obj.toString();
+            //throw assertUnexpectedType(obj);
         }
     }
 
-    @SuppressWarnings("unused")
     private static Double toDouble(final Object obj0) {
         // TODO - Order tests for performance.
         for (Object obj = obj0; ;) {
@@ -180,6 +200,12 @@
         }
     }
 
+    // TAL
+    private static double toDoublePrimitive(final Object obj0) {
+        final Double d = toDouble(obj0);
+        return d == null ? 0.0D : d;
+    }
+
     @SuppressWarnings("unused")
     private static Number toNumber(final Object obj0) {
         // TODO - Order tests for performance.
@@ -227,16 +253,194 @@
         }
     }
 
-    private static AssertionError assertUnexpectedType(final Object obj) {
-        return new AssertionError("Unexpected type" + obj.getClass().getName() 
+ ". Guards should have prevented this");
-    }
-
-    @SuppressWarnings("unused")
     private static long toLongPrimitive(final Object obj0) {
         final Long l = toLong(obj0);
         return l == null ? 0L : l;
     }
 
+    // TAL
+    @SuppressWarnings("unused")
+    private static Object[] toObjectArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               Object[] r = new Object[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = data.getObject(i);
+               }
+               return r;
+       } else {
+               return obj0 != null ? new Object[] { obj0 } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static String[] toStringArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               String[] r = new String[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = toString(data.getObject(i));
+               }
+               return r;
+       } else {
+               String r = toString(obj0);
+               return r != null ? new String[] { r } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static boolean[] toBooleanPrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               boolean[] r = new boolean[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = toBooleanPrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Boolean r = toBoolean(obj0);
+               return r != null ? new boolean[] { r } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static float[] toFloatPrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               float[] r = new float[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = (float) toDoublePrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Double r = toDouble(obj0);
+               return r != null ? new float[] { r.floatValue() } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static double[] toDoublePrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               double[] r = new double[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = toDoublePrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Double r = toDouble(obj0);
+               return r != null ? new double[] { r } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static byte[] toBytePrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               byte[] r = new byte[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = (byte) toLongPrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Long r = toLong(obj0);
+               return r != null ? new byte[] { r.byteValue() } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static short[] toShortPrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               short[] r = new short[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = (short) toLongPrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Long r = toLong(obj0);
+               return r != null ? new short[] { r.shortValue() } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static int[] toIntPrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               int[] r = new int[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = (int) toLongPrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Long r = toLong(obj0);
+               return r != null ? new int[] { r.intValue() } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static long[] toLongPrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               long[] r = new long[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = toLongPrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Long r = toLong(obj0);
+               return r != null ? new long[] { r } : null;
+       }
+    }
+
+    // TAL
+    @SuppressWarnings("unused")
+    private static char[] toCharPrimitiveArray(final Object obj0)
+    {
+       if (obj0 instanceof NativeArray) {
+               ArrayData data = ((NativeArray) obj0).getArray();
+               int length = (int) data.length();
+               char[] r = new char[length];
+               for (int i = 0; i < length; i++) {
+                       r[i] = toCharPrimitive(data.getObject(i));
+               }
+               return r;
+       } else {
+               Character r = toChar(obj0);
+               return r != null ? new char[] { r } : null;
+       }
+    }
+
+    private static AssertionError assertUnexpectedType(final Object obj) {
+        return new AssertionError("Unexpected type" + obj.getClass().getName() 
+ ". Guards should have prevented this");
+    }
+
     private static MethodHandle findOwnMH(final String name, final Class<?> 
rtype, final Class<?>... types) {
         return MH.findStatic(MethodHandles.lookup(), 
JavaArgumentConverters.class, name, MH.type(rtype, types));
     }
@@ -259,6 +463,18 @@
         CONVERTERS.put(long.class, TO_LONG_PRIMITIVE);
         CONVERTERS.put(Long.class, TO_LONG);
 
+        // TAL
+        CONVERTERS.put(Object[].class, TO_OBJECT_ARRAY);
+        CONVERTERS.put(String[].class, TO_STRING_ARRAY);
+        CONVERTERS.put(boolean[].class, TO_BOOLEAN_PRIMITIVE_ARRAY);
+        CONVERTERS.put(float[].class, TO_FLOAT_PRIMITIVE_ARRAY);
+        CONVERTERS.put(double[].class, TO_DOUBLE_PRIMITIVE_ARRAY);
+        CONVERTERS.put(byte[].class, TO_BYTE_PRIMITIVE_ARRAY);
+        CONVERTERS.put(short[].class, TO_SHORT_PRIMITIVE_ARRAY);
+        CONVERTERS.put(int[].class, TO_INT_PRIMITIVE_ARRAY);
+        CONVERTERS.put(long[].class, TO_LONG_PRIMITIVE_ARRAY);
+        CONVERTERS.put(char[].class, TO_CHAR_PRIMITIVE_ARRAY);
+
         putLongConverter(Byte.class);
         putLongConverter(Short.class);
         putLongConverter(Integer.class);
diff --git 
a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java 
b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
--- a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
@@ -29,6 +29,7 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+
 import jdk.internal.dynalink.linker.ConversionComparator;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
@@ -36,6 +37,7 @@
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
 import jdk.internal.dynalink.support.TypeUtilities;
+import jdk.nashorn.internal.objects.NativeArray;
 import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.GlobalObject;
@@ -52,7 +54,12 @@
     }
 
     private static boolean canLinkTypeStatic(final Class<?> type) {
-        return type == String.class || type == Boolean.class || type == 
ConsString.class || Number.class.isAssignableFrom(type);
+        return type == String.class || type == Boolean.class || type == 
ConsString.class || Number.class.isAssignableFrom(type) ||
+                       // TAL
+                       type == String[].class || type == Object[].class || 
type == boolean[].class ||
+                       type == float[].class || type == double[].class ||
+                       type == byte[].class || type == short[].class || type 
== int[].class || type == long[].class ||
+                       type == char[].class;
     }
 
     @Override
@@ -63,6 +70,12 @@
         final Object self = request.getReceiver();
         final GlobalObject global = (GlobalObject) Context.getGlobal();
         final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor) 
request.getCallSiteDescriptor();
+        
+        // TAL
+        if (self.getClass().isArray()) {
+            // TODO
+               return null;
+        }
 
         return Bootstrap.asType(global.primitiveLookup(request, self), 
linkerServices, desc);
     }
@@ -148,6 +161,16 @@
                 return Comparison.TYPE_2_BETTER;
             }
         }
+        
+        // TAL
+        if (NativeArray.class.isAssignableFrom(sourceType)) {
+               if (targetType1.isArray() && !targetType2.isArray()) {
+                       return Comparison.TYPE_1_BETTER;
+               }
+               if (targetType2.isArray() && !targetType1.isArray()) {
+                       return Comparison.TYPE_2_BETTER;
+               }
+        }
 
         return Comparison.INDETERMINATE;
     }

Reply via email to