8026113: Nashorn arrays should automatically convert to Java arrays

Mon, 14 Oct 2013 12:41:11 +0200

author
attila
date
Mon, 14 Oct 2013 12:41:11 +0200
changeset 621
d155c4a7703c
parent 620
8c617a092d68
child 622
64e841576c68

8026113: Nashorn arrays should automatically convert to Java arrays
Reviewed-by: jlaskey, sundar

src/jdk/nashorn/internal/runtime/JSType.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/runtime/JSType.java	Mon Oct 14 11:45:15 2013 +0200
     1.2 +++ b/src/jdk/nashorn/internal/runtime/JSType.java	Mon Oct 14 12:41:11 2013 +0200
     1.3 @@ -31,6 +31,8 @@
     1.4  import java.lang.invoke.MethodHandle;
     1.5  import java.lang.invoke.MethodHandles;
     1.6  import java.lang.reflect.Array;
     1.7 +import java.util.Deque;
     1.8 +import java.util.List;
     1.9  import jdk.internal.dynalink.beans.StaticClass;
    1.10  import jdk.nashorn.api.scripting.JSObject;
    1.11  import jdk.nashorn.internal.codegen.CompilerConstants.Call;
    1.12 @@ -110,6 +112,15 @@
    1.13      /** Combined call to toPrimitive followed by toString. */
    1.14      public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class,  Object.class);
    1.15  
    1.16 +    /** Method handle to convert a JS Object to a Java array. */
    1.17 +    public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
    1.18 +
    1.19 +    /** Method handle to convert a JS Object to a Java List. */
    1.20 +    public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class);
    1.21 +
    1.22 +    /** Method handle to convert a JS Object to a Java deque. */
    1.23 +   public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class);
    1.24 +
    1.25      private static final double INT32_LIMIT = 4294967296.0;
    1.26  
    1.27      /**
    1.28 @@ -890,6 +901,8 @@
    1.29                  res[idx++] = itr.next();
    1.30              }
    1.31              return convertArray(res, componentType);
    1.32 +        } else if(obj == null) {
    1.33 +            return null;
    1.34          } else {
    1.35              throw new IllegalArgumentException("not a script object");
    1.36          }
    1.37 @@ -919,6 +932,24 @@
    1.38      }
    1.39  
    1.40      /**
    1.41 +     * Converts a JavaScript object to a Java List. See {@link ListAdapter} for details.
    1.42 +     * @param obj the object to convert. Can be any array-like object.
    1.43 +     * @return a List that is live-backed by the JavaScript object.
    1.44 +     */
    1.45 +    public static List<?> toJavaList(final Object obj) {
    1.46 +        return ListAdapter.create(obj);
    1.47 +    }
    1.48 +
    1.49 +    /**
    1.50 +     * Converts a JavaScript object to a Java Deque. See {@link ListAdapter} for details.
    1.51 +     * @param obj the object to convert. Can be any array-like object.
    1.52 +     * @return a Deque that is live-backed by the JavaScript object.
    1.53 +     */
    1.54 +    public static Deque<?> toJavaDeque(final Object obj) {
    1.55 +        return ListAdapter.create(obj);
    1.56 +    }
    1.57 +
    1.58 +    /**
    1.59       * Check if an object is null or undefined
    1.60       *
    1.61       * @param obj object to check
     2.1 --- a/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java	Mon Oct 14 11:45:15 2013 +0200
     2.2 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaArgumentConverters.java	Mon Oct 14 12:41:11 2013 +0200
     2.3 @@ -25,14 +25,16 @@
     2.4  
     2.5  package jdk.nashorn.internal.runtime.linker;
     2.6  
     2.7 +import static jdk.nashorn.internal.lookup.Lookup.MH;
     2.8  import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
     2.9  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    2.10 -import static jdk.nashorn.internal.lookup.Lookup.MH;
    2.11  
    2.12  import java.lang.invoke.MethodHandle;
    2.13  import java.lang.invoke.MethodHandles;
    2.14 -import java.util.HashMap;
    2.15 -import java.util.Map;
    2.16 +import java.util.Deque;
    2.17 +import java.util.List;
    2.18 +import java.util.concurrent.ConcurrentHashMap;
    2.19 +import java.util.concurrent.ConcurrentMap;
    2.20  import jdk.internal.dynalink.support.TypeUtilities;
    2.21  import jdk.nashorn.internal.runtime.ConsString;
    2.22  import jdk.nashorn.internal.runtime.JSType;
    2.23 @@ -57,7 +59,13 @@
    2.24      }
    2.25  
    2.26      static MethodHandle getConverter(final Class<?> targetType) {
    2.27 -        return CONVERTERS.get(targetType);
    2.28 +        MethodHandle converter = CONVERTERS.get(targetType);
    2.29 +        if(converter ==  null && targetType.isArray()) {
    2.30 +            converter = MH.insertArguments(JSType.TO_JAVA_ARRAY.methodHandle(), 1, targetType.getComponentType());
    2.31 +            converter = MH.asType(converter, converter.type().changeReturnType(targetType));
    2.32 +            CONVERTERS.putIfAbsent(targetType, converter);
    2.33 +        }
    2.34 +        return converter;
    2.35      }
    2.36  
    2.37      @SuppressWarnings("unused")
    2.38 @@ -211,6 +219,20 @@
    2.39                  return null;
    2.40              } else if (obj instanceof Long) {
    2.41                  return (Long) obj;
    2.42 +            } else if (obj instanceof Integer) {
    2.43 +                return ((Integer)obj).longValue();
    2.44 +            } else if (obj instanceof Double) {
    2.45 +                final Double d = (Double)obj;
    2.46 +                if(Double.isInfinite(d.doubleValue())) {
    2.47 +                    return 0L;
    2.48 +                }
    2.49 +                return d.longValue();
    2.50 +            } else if (obj instanceof Float) {
    2.51 +                final Float f = (Float)obj;
    2.52 +                if(Float.isInfinite(f.floatValue())) {
    2.53 +                    return 0L;
    2.54 +                }
    2.55 +                return f.longValue();
    2.56              } else if (obj instanceof Number) {
    2.57                  return ((Number)obj).longValue();
    2.58              } else if (obj instanceof String || obj instanceof ConsString) {
    2.59 @@ -241,9 +263,12 @@
    2.60          return MH.findStatic(MethodHandles.lookup(), JavaArgumentConverters.class, name, MH.type(rtype, types));
    2.61      }
    2.62  
    2.63 -    private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>();
    2.64 +    private static final ConcurrentMap<Class<?>, MethodHandle> CONVERTERS = new ConcurrentHashMap<>();
    2.65  
    2.66      static {
    2.67 +        CONVERTERS.put(List.class, JSType.TO_JAVA_LIST.methodHandle());
    2.68 +        CONVERTERS.put(Deque.class, JSType.TO_JAVA_DEQUE.methodHandle());
    2.69 +
    2.70          CONVERTERS.put(Number.class, TO_NUMBER);
    2.71          CONVERTERS.put(String.class, TO_STRING);
    2.72  
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/src/jdk/nashorn/api/javaaccess/ArrayConversionTest.java	Mon Oct 14 12:41:11 2013 +0200
     3.3 @@ -0,0 +1,191 @@
     3.4 +/*
     3.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.7 + *
     3.8 + * This code is free software; you can redistribute it and/or modify it
     3.9 + * under the terms of the GNU General Public License version 2 only, as
    3.10 + * published by the Free Software Foundation.  Oracle designates this
    3.11 + * particular file as subject to the "Classpath" exception as provided
    3.12 + * by Oracle in the LICENSE file that accompanied this code.
    3.13 + *
    3.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    3.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    3.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    3.17 + * version 2 for more details (a copy is included in the LICENSE file that
    3.18 + * accompanied this code).
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License version
    3.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    3.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    3.23 + *
    3.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    3.25 + * or visit www.oracle.com if you need additional information or have any
    3.26 + * questions.
    3.27 + */
    3.28 +
    3.29 +package jdk.nashorn.api.javaaccess;
    3.30 +
    3.31 +import static org.testng.AssertJUnit.assertEquals;
    3.32 +import static org.testng.AssertJUnit.assertFalse;
    3.33 +import static org.testng.AssertJUnit.assertNull;
    3.34 +import static org.testng.AssertJUnit.assertTrue;
    3.35 +
    3.36 +import java.util.Arrays;
    3.37 +import java.util.List;
    3.38 +import javax.script.ScriptContext;
    3.39 +import javax.script.ScriptEngine;
    3.40 +import javax.script.ScriptEngineManager;
    3.41 +import javax.script.ScriptException;
    3.42 +import org.testng.TestNG;
    3.43 +import org.testng.annotations.AfterClass;
    3.44 +import org.testng.annotations.BeforeClass;
    3.45 +import org.testng.annotations.Test;
    3.46 +
    3.47 +public class ArrayConversionTest {
    3.48 +    private static ScriptEngine e = null;
    3.49 +
    3.50 +    public static void main(final String[] args) {
    3.51 +        TestNG.main(args);
    3.52 +    }
    3.53 +
    3.54 +    @BeforeClass
    3.55 +    public static void setUpClass() throws ScriptException {
    3.56 +        e = new ScriptEngineManager().getEngineByName("nashorn");
    3.57 +    }
    3.58 +
    3.59 +    @AfterClass
    3.60 +    public static void tearDownClass() {
    3.61 +        e = null;
    3.62 +    }
    3.63 +
    3.64 +    @Test
    3.65 +    public void testIntArrays() throws ScriptException {
    3.66 +        runTest("assertNullIntArray", "null");
    3.67 +        runTest("assertEmptyIntArray", "[]");
    3.68 +        runTest("assertSingle42IntArray", "[42]");
    3.69 +        runTest("assertSingle42IntArray", "['42']");
    3.70 +        runTest("assertIntArrayConversions", "[false, true, NaN, Infinity, -Infinity, 0.4, 0.6, null, undefined, [], {}, [1], [1, 2]]");
    3.71 +    }
    3.72 +
    3.73 +    @Test
    3.74 +    public void testIntIntArrays() throws ScriptException {
    3.75 +        runTest("assertNullIntIntArray", "null");
    3.76 +        runTest("assertEmptyIntIntArray", "[]");
    3.77 +        runTest("assertSingleEmptyIntIntArray", "[[]]");
    3.78 +        runTest("assertSingleNullIntIntArray", "[null]");
    3.79 +        runTest("assertLargeIntIntArray", "[[false], [1], [2, 3], [4, 5, 6], ['7', {valueOf: function() { return 8 }}]]");
    3.80 +    }
    3.81 +
    3.82 +    @Test
    3.83 +    public void testObjectObjectArrays() throws ScriptException {
    3.84 +        runTest("assertLargeObjectObjectArray", "[[false], [1], ['foo', 42.3], [{x: 17}]]");
    3.85 +    }
    3.86 +
    3.87 +    @Test
    3.88 +    public void testBooleanArrays() throws ScriptException {
    3.89 +        runTest("assertBooleanArrayConversions", "[false, true, '', 'false', 0, 1, 0.4, 0.6, {}, [], [false], [true], NaN, Infinity, null, undefined]");
    3.90 +    }
    3.91 +
    3.92 +    @Test
    3.93 +    public void testListArrays() throws ScriptException {
    3.94 +        runTest("assertListArray", "[['foo', 'bar'], ['apple', 'orange']]");
    3.95 +    }
    3.96 +
    3.97 +    private static void runTest(final String testMethodName, final String argument) throws ScriptException {
    3.98 +        e.eval("Java.type('" + ArrayConversionTest.class.getName() + "')." + testMethodName + "(" + argument + ")");
    3.99 +    }
   3.100 +
   3.101 +    public static void assertNullIntArray(int[] array) {
   3.102 +        assertNull(array);
   3.103 +    }
   3.104 +
   3.105 +    public static void assertNullIntIntArray(int[][] array) {
   3.106 +        assertNull(array);
   3.107 +    }
   3.108 +
   3.109 +    public static void assertEmptyIntArray(int[] array) {
   3.110 +        assertEquals(0, array.length);
   3.111 +    }
   3.112 +
   3.113 +    public static void assertSingle42IntArray(int[] array) {
   3.114 +        assertEquals(1, array.length);
   3.115 +        assertEquals(42, array[0]);
   3.116 +    }
   3.117 +
   3.118 +
   3.119 +    public static void assertIntArrayConversions(int[] array) {
   3.120 +        assertEquals(13, array.length);
   3.121 +        assertEquals(0, array[0]); // false
   3.122 +        assertEquals(1, array[1]); // true
   3.123 +        assertEquals(0, array[2]); // NaN
   3.124 +        assertEquals(0, array[3]); // Infinity
   3.125 +        assertEquals(0, array[4]); // -Infinity
   3.126 +        assertEquals(0, array[5]); // 0.4
   3.127 +        assertEquals(0, array[6]); // 0.6 - floor, not round
   3.128 +        assertEquals(0, array[7]); // null
   3.129 +        assertEquals(0, array[8]); // undefined
   3.130 +        assertEquals(0, array[9]); // []
   3.131 +        assertEquals(0, array[10]); // {}
   3.132 +        assertEquals(1, array[11]); // [1]
   3.133 +        assertEquals(0, array[12]); // [1, 2]
   3.134 +    }
   3.135 +
   3.136 +    public static void assertEmptyIntIntArray(int[][] array) {
   3.137 +        assertEquals(0, array.length);
   3.138 +    }
   3.139 +
   3.140 +    public static void assertSingleEmptyIntIntArray(int[][] array) {
   3.141 +        assertEquals(1, array.length);
   3.142 +        assertTrue(Arrays.equals(new int[0], array[0]));
   3.143 +    }
   3.144 +
   3.145 +    public static void assertSingleNullIntIntArray(int[][] array) {
   3.146 +        assertEquals(1, array.length);
   3.147 +        assertNull(null, array[0]);
   3.148 +    }
   3.149 +
   3.150 +    public static void assertLargeIntIntArray(int[][] array) {
   3.151 +        assertEquals(5, array.length);
   3.152 +        assertTrue(Arrays.equals(new int[] { 0 }, array[0]));
   3.153 +        assertTrue(Arrays.equals(new int[] { 1 }, array[1]));
   3.154 +        assertTrue(Arrays.equals(new int[] { 2, 3 }, array[2]));
   3.155 +        assertTrue(Arrays.equals(new int[] { 4, 5, 6 }, array[3]));
   3.156 +        assertTrue(Arrays.equals(new int[] { 7, 8 }, array[4]));
   3.157 +    }
   3.158 +
   3.159 +    public static void assertLargeObjectObjectArray(Object[][] array) throws ScriptException {
   3.160 +        assertEquals(4, array.length);
   3.161 +        assertTrue(Arrays.equals(new Object[] { Boolean.FALSE }, array[0]));
   3.162 +        assertTrue(Arrays.equals(new Object[] { 1 }, array[1]));
   3.163 +        assertTrue(Arrays.equals(new Object[] { "foo", 42.3d }, array[2]));
   3.164 +        assertEquals(1, array[3].length);
   3.165 +        e.getBindings(ScriptContext.ENGINE_SCOPE).put("obj", array[3][0]);
   3.166 +        assertEquals(17, e.eval("obj.x"));
   3.167 +    }
   3.168 +
   3.169 +    public static void assertBooleanArrayConversions(boolean[] array) {
   3.170 +        assertEquals(16, array.length);
   3.171 +        assertFalse(array[0]); // false
   3.172 +        assertTrue(array[1]); // true
   3.173 +        assertFalse(array[2]); // ''
   3.174 +        assertTrue(array[3]); // 'false' (yep, every non-empty string converts to true)
   3.175 +        assertFalse(array[4]); // 0
   3.176 +        assertTrue(array[5]); // 1
   3.177 +        assertTrue(array[6]); // 0.4
   3.178 +        assertTrue(array[7]); // 0.6
   3.179 +        assertTrue(array[8]); // {}
   3.180 +        assertTrue(array[9]); // []
   3.181 +        assertTrue(array[10]); // [false]
   3.182 +        assertTrue(array[11]); // [true]
   3.183 +        assertFalse(array[12]); // NaN
   3.184 +        assertTrue(array[13]); // Infinity
   3.185 +        assertFalse(array[14]); // null
   3.186 +        assertFalse(array[15]); // undefined
   3.187 +    }
   3.188 +
   3.189 +    public static void assertListArray(List<?>[] array) {
   3.190 +        assertEquals(2, array.length);
   3.191 +        assertEquals(Arrays.asList("foo", "bar"), array[0]);
   3.192 +        assertEquals(Arrays.asList("apple", "orange"), array[1]);
   3.193 +    }
   3.194 +}

mercurial