changeset 22523:6ab540203853

Expanding TCK to cover reading and writing of object properties with various primitive types
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Tue, 22 Dec 2015 18:00:04 +0100
parents cda3eebfa777
children 579d21e36582
files truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/interop/SLForeignToSLTypeNode.java truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java
diffstat 4 files changed, 228 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java	Mon Dec 21 11:11:45 2015 +0100
+++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java	Tue Dec 22 18:00:04 2015 +0100
@@ -273,15 +273,16 @@
     }
 
     static Object toJava(Object ret, Method method) {
-        if (isPrimitive(ret)) {
-            return ret;
+        Class<?> retType = method.getReturnType();
+        Object primitiveRet = toPrimitive(ret, retType);
+        if (primitiveRet != null) {
+            return primitiveRet;
         }
         if (ret instanceof TruffleObject) {
             if (Boolean.TRUE.equals(message(Message.IS_NULL, ret))) {
                 return null;
             }
         }
-        Class<?> retType = method.getReturnType();
         if (retType.isInstance(ret)) {
             return ret;
         }
@@ -397,8 +398,9 @@
                     ret = message(Message.createInvoke(args.length), obj, callArgs.toArray());
                 } catch (IllegalArgumentException ex) {
                     val = message(Message.READ, obj, name);
-                    if (isPrimitive(val)) {
-                        return val;
+                    Object primitiveVal = toPrimitive(val, method.getReturnType());
+                    if (primitiveVal != null) {
+                        return primitiveVal;
                     }
                     TruffleObject attr = (TruffleObject) val;
                     if (Boolean.FALSE.equals(message(Message.IS_EXECUTABLE, attr))) {
@@ -419,22 +421,56 @@
     }
 
     static boolean isPrimitive(Object attr) {
+        return toPrimitive(attr, null) != null;
+    }
+
+    static Object toPrimitive(Object attr, Class<?> requestedType) {
         if (attr instanceof TruffleObject) {
-            return false;
+            return null;
         }
         if (attr instanceof Number) {
-            return true;
+            if (requestedType == null) {
+                return attr;
+            }
+            Number n = (Number) attr;
+            if (requestedType == byte.class || requestedType == Byte.class) {
+                return n.byteValue();
+            }
+            if (requestedType == short.class || requestedType == Short.class) {
+                return n.shortValue();
+            }
+            if (requestedType == int.class || requestedType == Integer.class) {
+                return n.intValue();
+            }
+            if (requestedType == long.class || requestedType == Long.class) {
+                return n.longValue();
+            }
+            if (requestedType == float.class || requestedType == Float.class) {
+                return n.floatValue();
+            }
+            if (requestedType == double.class || requestedType == Double.class) {
+                return n.doubleValue();
+            }
+            if (requestedType == char.class || requestedType == Character.class) {
+                return (char) n.intValue();
+            }
+            return n;
         }
         if (attr instanceof String) {
-            return true;
+            if (requestedType == char.class || requestedType == Character.class) {
+                if (((String) attr).length() == 1) {
+                    return ((String) attr).charAt(0);
+                }
+            }
+            return attr;
         }
         if (attr instanceof Character) {
-            return true;
+            return attr;
         }
         if (attr instanceof Boolean) {
-            return true;
+            return attr;
         }
-        return false;
+        return null;
     }
 
     static Object message(final Message m, Object receiver, Object... arr) {
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Mon Dec 21 11:11:45 2015 +0100
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Tue Dec 22 18:00:04 2015 +0100
@@ -113,7 +113,20 @@
                 "  obj.returnsNull = returnsNull;\n" +
                 "  obj.returnsThis = obj;\n" +
                 "  return obj;\n" +
-                "}\n", "SL TCK"
+                "}\n" +
+                "function valuesObject() {\n" +
+                "  obj = new();\n" +
+                "  obj.byteValue = 0;\n" +
+                "  obj.shortValue = 0;\n" +
+                "  obj.intValue = 0;\n" +
+                "  obj.longValue = 0;\n" +
+                "  obj.floatValue = 0;\n" +
+                "  obj.doubleValue = 0;\n" +
+                "  obj.charValue = \"0\";\n" +
+                "  obj.booleanValue = (1 == 0);\n" +
+                "  return obj;\n" +
+                "}\n",
+                "SL TCK"
             ).withMimeType("application/x-sl")
         );
         // @formatter:on
@@ -156,6 +169,11 @@
     }
 
     @Override
+    protected String valuesObject() {
+        return "valuesObject";
+    }
+
+    @Override
     protected String invalidCode() {
         // @formatter:off
         return
@@ -251,4 +269,11 @@
     public void testCopyStructuredComplexToComplexNumbersA() throws Exception {
     }
 
+    @Override
+    public void readWriteDoubleValue() throws Exception {
+    }
+
+    @Override
+    public void readWriteFloatValue() throws Exception {
+    }
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/interop/SLForeignToSLTypeNode.java	Mon Dec 21 11:11:45 2015 +0100
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/interop/SLForeignToSLTypeNode.java	Tue Dec 22 18:00:04 2015 +0100
@@ -70,6 +70,16 @@
         return value;
     }
 
+    @Specialization
+    public Object fromBoolean(boolean value) {
+        return value;
+    }
+
+    @Specialization
+    public Object fromChar(char value) {
+        return String.valueOf(value);
+    }
+
     @Specialization(guards = "isBoxedPrimitive(frame, value)")
     public Object unbox(VirtualFrame frame, TruffleObject value) {
         Object unboxed = doUnbox(frame, value);
--- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Mon Dec 21 11:11:45 2015 +0100
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Tue Dec 22 18:00:04 2015 +0100
@@ -44,6 +44,7 @@
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.interop.java.MethodMessage;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.api.vm.PolyglotEngine.Language;
@@ -116,7 +117,7 @@
 
     /**
      * Name of function to add two numbers together. The symbol will be invoked with two parameters
-     * of <code>type1</code> and <code>type2</code> and expects result of type {@link Number}
+     * of <code>type1</code> and <code>type2</code> and expects result of type {@link Number} 
      * which's {@link Number#intValue()} is equivalent of <code>param1 + param2</code>. As some
      * languages may have different operations for different types of numbers, the actual types are
      * passed to the method and the implementation can decide to return different symbol based on
@@ -271,6 +272,35 @@
         throw new UnsupportedOperationException("compoundObject() method not implemented");
     }
 
+    /**
+     * Name of a function that returns a compound object with members representing certain primitive
+     * types. In the JavaScript the object should look like:
+     *
+     * <pre>
+     * <b>var</b> obj = {
+     *   'byteValue': 0,
+     *   'shortValue': 0,
+     *   'intValue': 0,
+     *   'longValue': 0,
+     *   'floatValue': 0.0,
+     *   'doubleValue': 0.0,
+     *   'charValue': '0',
+     *   'stringValue': '',
+     *   'booleanVlaue': false
+     * };
+     * <b>return</b> obj;
+     * </pre>
+     *
+     * The returned object shall have slots for these values that can be read and written to.
+     * Various test methods try to read and modify the values. Each invocation of the function
+     * should yield new object.
+     *
+     * @return name of a function that returns such values object
+     */
+    protected String valuesObject() {
+        throw new UnsupportedOperationException("valuesObject() method not implemented");
+    }
+
     private PolyglotEngine vm() throws Exception {
         if (tckVM == null) {
             tckVM = prepareVM();
@@ -942,6 +972,78 @@
         Assert.assertArrayEquals(new double[]{41, 42, 43, 44, 45, 46}, a.getData(), 0.1);
     }
 
+    @Test
+    public void readWriteByteValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("Zero", 0, values.byteValue());
+        final byte value = (byte) RANDOM.nextInt(128);
+        values.byteValue(value);
+        assertEquals("Correct value", value, values.byteValue());
+    }
+
+    @Test
+    public void readWriteShortValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("Zero", 0, values.shortValue());
+        final short value = (short) RANDOM.nextInt(32768);
+        values.shortValue(value);
+        assertEquals("Correct value", value, values.shortValue());
+    }
+
+    @Test
+    public void readWriteIntValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("Zero", 0, values.intValue());
+        final int value = RANDOM.nextInt();
+        values.intValue(value);
+        assertEquals("Correct value", value, values.intValue());
+    }
+
+    @Test
+    public void readWriteFloatValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("Zero", 0, values.floatValue(), 0.1);
+        final float value = RANDOM.nextFloat() * 1000.0f;
+        values.floatValue(value);
+        assertEquals("Correct value", value, values.floatValue(), 0.1);
+    }
+
+    @Test
+    public void readWriteDoubleValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("Zero", 0, values.doubleValue(), 0.1);
+        final double value = RANDOM.nextDouble() * 1000.0;
+        values.doubleValue(value);
+        assertEquals("Correct value", value, values.doubleValue(), 0.1);
+    }
+
+    @Test
+    public void readWriteCharValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("Zero", '0', values.charValue());
+        String letters = "P\u0159\u00EDli\u0161 \u017Elu\u0165ou\u010Dk\u00FD k\u016F\u0148 \u00FAp\u011Bl \u010F\u00E1belsk\u00E9 \u00F3dy";
+        final char value = letters.charAt(RANDOM.nextInt(letters.length()));
+        values.charValue(value);
+        assertEquals("Correct value", value, values.charValue());
+    }
+
+    @Test
+    public void readWriteBooleanValue() throws Exception {
+        String id = valuesObject();
+        ValuesObject values = findGlobalSymbol(id).execute().as(ValuesObject.class);
+        assertEquals("False", false, values.booleanValue());
+        values.booleanValue(true);
+        assertEquals("Correct value", true, values.booleanValue());
+        values.booleanValue(false);
+        assertEquals("Correct value2", false, values.booleanValue());
+    }
+
     private static void putDoubles(byte[] buffer, double[] values) {
         for (int index = 0; index < values.length; index++) {
             int doubleSize = Double.SIZE / Byte.SIZE;
@@ -1005,4 +1107,46 @@
 
         CompoundObject returnsThis();
     }
+
+    interface ValuesObject {
+        byte byteValue();
+
+        @MethodMessage(message = "WRITE")
+        void byteValue(byte v);
+
+        short shortValue();
+
+        @MethodMessage(message = "WRITE")
+        void shortValue(short v);
+
+        int intValue();
+
+        @MethodMessage(message = "WRITE")
+        void intValue(int v);
+
+        long longValue();
+
+        @MethodMessage(message = "WRITE")
+        void longValue(long v);
+
+        float floatValue();
+
+        @MethodMessage(message = "WRITE")
+        void floatValue(float v);
+
+        double doubleValue();
+
+        @MethodMessage(message = "WRITE")
+        void doubleValue(double v);
+
+        char charValue();
+
+        @MethodMessage(message = "WRITE")
+        void charValue(char v);
+
+        boolean booleanValue();
+
+        @MethodMessage(message = "WRITE")
+        void booleanValue(boolean v);
+    }
 }