# HG changeset patch # User Jaroslav Tulach # Date 1450803604 -3600 # Node ID 6ab54020385366f1ae8b86e782358f25cddde93a # Parent cda3eebfa77711fe5704f42f0827cbe4c61b8032 Expanding TCK to cover reading and writing of object properties with various primitive types diff -r cda3eebfa777 -r 6ab540203853 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java --- 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) { diff -r cda3eebfa777 -r 6ab540203853 truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java --- 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 { + } } diff -r cda3eebfa777 -r 6ab540203853 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/interop/SLForeignToSLTypeNode.java --- 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); diff -r cda3eebfa777 -r 6ab540203853 truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java --- 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 type1 and type2 and expects result of type {@link Number} + * of type1 and type2 and expects result of type {@link Number} * which's {@link Number#intValue()} is equivalent of param1 + param2. 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: + * + *
+     * var obj = {
+     *   'byteValue': 0,
+     *   'shortValue': 0,
+     *   'intValue': 0,
+     *   'longValue': 0,
+     *   'floatValue': 0.0,
+     *   'doubleValue': 0.0,
+     *   'charValue': '0',
+     *   'stringValue': '',
+     *   'booleanVlaue': false
+     * };
+     * return obj;
+     * 
+ * + * 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); + } }