# HG changeset patch # User Jaroslav Tulach # Date 1441638440 -7200 # Node ID e70b20f4bb00133fcf25c41ac11bcd4e0767f109 # Parent 025869c88840eeafb196982806ef1f74264b5743 Implementing API for Java/Truffle interop. Based around JavaInterop.asJavaObject and JavaInterop.asTruffleObject methods. Connected to TruffleVM via Symbol.as(Class) wrapper. Verified by extended TCK. diff -r 025869c88840 -r e70b20f4bb00 mx.truffle/suite.py --- a/mx.truffle/suite.py Fri Sep 04 16:41:38 2015 +0200 +++ b/mx.truffle/suite.py Mon Sep 07 17:07:20 2015 +0200 @@ -49,7 +49,7 @@ "subDir" : "truffle", "sourceDirs" : ["src"], "dependencies" : [ - "com.oracle.truffle.api.interop", + "com.oracle.truffle.api.interop.java", ], "javaCompliance" : "1.7", "workingSets" : "API,Truffle", @@ -111,6 +111,30 @@ "workingSets" : "API,Truffle", }, + "com.oracle.truffle.api.interop.java" : { + "subDir" : "truffle", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.truffle.api.interop", + "com.oracle.truffle.api.dsl" + ], + "checkstyle" : "com.oracle.truffle.api", + "javaCompliance" : "1.7", + "workingSets" : "API,Truffle", + }, + + "com.oracle.truffle.api.interop.java.test" : { + "subDir" : "truffle", + "sourceDirs" : ["src"], + "dependencies" : [ + "com.oracle.truffle.api.interop.java", + "mx:JUNIT" + ], + "checkstyle" : "com.oracle.truffle.api", + "javaCompliance" : "1.7", + "workingSets" : "API,Truffle", + }, + "com.oracle.truffle.api.object" : { "subDir" : "truffle", "sourceDirs" : ["src"], @@ -236,6 +260,7 @@ "sourcesPath" : "build/truffle-api.src.zip", "javaCompliance" : "1.7", "dependencies" : [ + "com.oracle.truffle.api.interop.java", "com.oracle.truffle.api.dsl", "com.oracle.truffle.api.vm", "com.oracle.truffle.object.basic", diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; + +public class ClassInteropTest { + private TruffleObject obj; + private XYPlus xyp; + + public static int x; + public static double y; + public static final int CONST = 42; + public int value = CONST; + + public static double plus(double a, double b) { + return a + b; + } + + @Before + public void initObjects() { + obj = JavaInterop.asTruffleObject(ClassInteropTest.class); + xyp = JavaInterop.asJavaObject(XYPlus.class, obj); + } + + @Test + public void doubleWrap() { + x = 32; + y = 10.1; + assertEquals("Assume delegated", 42.1d, xyp.plus(xyp.x(), xyp.y()), 0.05); + } + + @Test + public void readCONST() { + assertEquals("Field read", 42, xyp.CONST(), 0.01); + } + + @Test + public void cannotReadValueAsItIsNotStatic() { + try { + assertEquals("Field read", 42, xyp.value()); + } catch (NoSuchFieldError ex) { + // OK + return; + } + fail("value isn't static field"); + } + + @Test + public void canReadValueAfterCreatingNewInstance() { + Object objInst = JavaInteropTest.message(Message.createNew(0), obj); + assertTrue("It is truffle object", objInst instanceof TruffleObject); + XYPlus inst = JavaInterop.asJavaObject(XYPlus.class, (TruffleObject) objInst); + assertEquals("Field read", 42, inst.value()); + } + + @Test(expected = NoSuchFieldError.class) + public void noNonStaticMethods() { + Object res = JavaInteropTest.message(Message.READ, obj, "readCONST"); + assertNull("not found", res); + } + + public interface XYPlus { + int x(); + + double y(); + + double plus(double a, double b); + + // Checkstyle: stop method name check + int CONST(); + + // Checkstyle: resume method name check + + int value(); + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/IntBinaryOperation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/IntBinaryOperation.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +/** + * Binary operation on integers. Mimics "functional interface" - e.g. has just a single method, so + * it should be easily usable with lamdas. + */ +public interface IntBinaryOperation { + int compute(int a, int b); +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; +import java.util.Random; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class JavaInteropSpeedTest { + private static final int REPEAT = 10000; + private static int[] arr; + private static long javaTime; + private static long interopTime; + + @BeforeClass + public static void beforeTesting() { + arr = initArray(REPEAT); + for (int i = 0; i < 1000; i++) { + JavaInteropSpeedTest t = new JavaInteropSpeedTest(); + t.doMinMaxInJava(); + t.doMinMaxWithInterOp(); + t.assertSame(); + } + } + + private int mmInOp; + private int mmInJava; + + @Test + public void doMinMaxInJava() { + int max = 0; + long now = System.currentTimeMillis(); + for (int i = 0; i < arr.length; i++) { + max = Math.max(arr[i], max); + } + javaTime = System.currentTimeMillis() - now; + mmInJava = max; + } + + private void assertSame() { + assertEquals(mmInJava, mmInOp); + } + + private static final TruffleObject TRUFFLE_MAX = JavaInterop.asTruffleFunction(IntBinaryOperation.class, new IntBinaryOperation() { + @Override + public int compute(int a, int b) { + return Math.max(a, b); + } + }); + private static final IntBinaryOperation MAX = JavaInterop.asJavaFunction(IntBinaryOperation.class, TRUFFLE_MAX); + + @Test + public void doMinMaxWithInterOp() { + int max = 0; + long now = System.currentTimeMillis(); + for (int i = 0; i < arr.length; i++) { + max = MAX.compute(arr[i], max); + } + interopTime = System.currentTimeMillis() - now; + mmInOp = max; + } + + @AfterClass + public static void nonSignificanDifference() { + if (javaTime < 1) { + javaTime = 1; + } + if (interopTime > 5 * javaTime) { + fail("Interop took too long: " + interopTime + " ms, while java only " + javaTime + " ms"); + } + } + + private static int[] initArray(int size) { + Random r = new Random(); + int[] tmp = new int[size]; + for (int i = 0; i < tmp.length; i++) { + tmp[i] = r.nextInt(100000); + } + return tmp; + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; +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.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import java.util.List; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; + +public class JavaInteropTest { + public int x; + public double y; + public String[] arr; + public Object value; + + private TruffleObject obj; + private XYPlus xyp; + private boolean assertThisCalled; + + public double plus(double a, double b) { + return a + b; + } + + public Object assertThis(Object param) { + assertSame("When a Java object is passed into Truffle and back, it is again the same object", this, param); + assertThisCalled = true; + return this; + } + + @Before + public void initObjects() { + obj = JavaInterop.asTruffleObject(this); + xyp = JavaInterop.asJavaObject(XYPlus.class, obj); + } + + @Test + public void doubleWrap() { + x = 32; + y = 10.1; + assertEquals("Assume delegated", 42.1d, xyp.plus(xyp.x(), xyp.y()), 0.05); + } + + @Test + public void writeX() { + xyp.x(10); + assertEquals("Changed", 10, x); + } + + @Test + public void assertThisIsSame() { + assertThisCalled = false; + XYPlus anotherThis = xyp.assertThis(this); + assertTrue(assertThisCalled); + + x = 44; + assertEquals(44, anotherThis.x()); + + assertEquals("The two proxies are equal", anotherThis, xyp); + } + + @Test + public void javaObjectsWrappedForTruffle() { + Object ret = message(Message.createInvoke(1), obj, "assertThis", obj); + assertTrue("Expecting truffle wrapper: " + ret, ret instanceof TruffleObject); + assertEquals("Same as this obj", ret, obj); + } + + @Test + public void arrayHasSize() { + arr = new String[]{"Hello", "World", "!"}; + Object arrObj = message(Message.READ, obj, "arr"); + assertTrue("It's obj: " + arrObj, arrObj instanceof TruffleObject); + TruffleObject truffleArr = (TruffleObject) arrObj; + assertEquals("It has size", Boolean.TRUE, message(Message.HAS_SIZE, truffleArr)); + assertEquals("Three elements", 3, message(Message.GET_SIZE, truffleArr)); + assertEquals("Hello", message(Message.READ, truffleArr, 0)); + assertEquals("World", message(Message.READ, truffleArr, 1)); + assertEquals("!", message(Message.READ, truffleArr, 2)); + } + + @Test + public void arrayAsList() { + arr = new String[]{"Hello", "World", "!"}; + List list = xyp.arr(); + assertEquals("Three elements", 3, list.size()); + assertEquals("Hello", list.get(0)); + assertEquals("World", list.get(1)); + assertEquals("!", list.get(2)); + + list.set(1, "there"); + + assertEquals("there", arr[1]); + } + + @Test + public void nullCanBeReturned() { + assertNull(xyp.value()); + } + + @Test + public void integerCanBeConvertedFromAnObjectField() { + value = 42; + assertEquals((Integer) 42, xyp.value()); + } + + public interface XYPlus { + List arr(); + + int x(); + + @MethodMessage(message = "WRITE") + void x(int v); + + double y(); + + double plus(double a, double b); + + Integer value(); + + XYPlus assertThis(Object obj); + } + + static Object message(final Message m, TruffleObject receiver, Object... arr) { + Node n = m.createNode(); + CallTarget callTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(TruffleLanguage.class, n, receiver, arr)); + return callTarget.call(); + } + + private static class TemporaryRoot extends RootNode { + @Node.Child private Node foreignAccess; + private final TruffleObject function; + private final Object[] args; + + @SuppressWarnings("rawtypes") + public TemporaryRoot(Class lang, Node foreignAccess, TruffleObject function, Object... args) { + super(lang, null, null); + this.foreignAccess = foreignAccess; + this.function = function; + this.args = args; + } + + @Override + public Object execute(VirtualFrame frame) { + return ForeignAccess.execute(foreignAccess, frame, function, args); + } + } // end of TemporaryRoot + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; +import com.oracle.truffle.api.interop.java.MethodMessage; +import static org.junit.Assert.*; +import org.junit.Test; + +public class MethodMessageTest { + interface MathWrap { + @MethodMessage(message = "READ") + MaxFunction max(); + } + + interface MaxFunction { + @MethodMessage(message = "IS_NULL") + boolean isNull(); + + @MethodMessage(message = "HAS_SIZE") + boolean isArray(); + + @MethodMessage(message = "GET_SIZE") + int size(); + + @MethodMessage(message = "IS_EXECUTABLE") + boolean canExecute(); + + @MethodMessage(message = "EXECUTE") + Number compute(int a, int b); + } + + @Test + public void functionTest() throws Exception { + TruffleObject truffleMath = JavaInterop.asTruffleObject(Math.class); + MathWrap wrap = JavaInterop.asJavaObject(MathWrap.class, truffleMath); + MaxFunction functionArityTwo = wrap.max(); + assertTrue("function can be executed", functionArityTwo.canExecute()); + assertFalse("function isn't null", functionArityTwo.isNull()); + assertFalse("function isn't array", functionArityTwo.isArray()); + int res = functionArityTwo.compute(10, 5).intValue(); + assertEquals(10, res); + } + + @Test + public void workWithAnArray() throws Exception { + TruffleObject arr = JavaInterop.asTruffleObject(new Object[]{1, 2, 3}); + + MaxFunction wrap = JavaInterop.asJavaObject(MaxFunction.class, arr); + + assertTrue("It is an array", wrap.isArray()); + assertFalse("No function", wrap.canExecute()); + assertFalse("Not null", wrap.isNull()); + + assertEquals("Size is 3", 3, wrap.size()); + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrimitiveArrayInteropTest { + public Object[] stringArr; + public byte[] byteArr; + public short[] shortArr; + public int[] intArr; + public long[] longArr; + public float[] floatArr; + public double[] doubleArr; + public char[] charArr; + public boolean[] boolArr; + + public interface ExactMatchInterop { + List stringArr(); + + List byteArr(); + + List shortArr(); + + List intArr(); + + List longArr(); + + List floatArr(); + + List doubleArr(); + + List charArr(); + + List boolArr(); + } + + private TruffleObject obj; + private ExactMatchInterop interop; + + @Before + public void initObjects() { + obj = JavaInterop.asTruffleObject(this); + interop = JavaInterop.asJavaObject(ExactMatchInterop.class, obj); + } + + @Test + public void everyThingIsNull() { + assertNull(interop.stringArr()); + assertNull(interop.byteArr()); + assertNull(interop.shortArr()); + assertNull(interop.intArr()); + assertNull(interop.longArr()); + assertNull(interop.floatArr()); + assertNull(interop.doubleArr()); + assertNull(interop.charArr()); + assertNull(interop.boolArr()); + } + + @Test + @SuppressWarnings({"unchecked", "rawtypes"}) + public void stringAsList() { + stringArr = new Object[]{"Hello", "World", "!"}; + List list = interop.stringArr(); + assertEquals("Three elements", 3, list.size()); + assertEquals("Hello", list.get(0)); + assertEquals("World", list.get(1)); + assertEquals("!", list.get(2)); + + list.set(1, "there"); + assertEquals("there", stringArr[1]); + + list.set(0, null); + assertNull("set to null", stringArr[0]); + + List rawList = list; + try { + rawList.set(0, 42); + } catch (ClassCastException ex) { + // OK + } + assertNull("still set to null", stringArr[0]); + } + + @Test + public void charOp() { + charArr = new char[]{'A', 'h', 'o', 'j'}; + assertEquals('j', (char) interop.charArr().get(3)); + interop.charArr().set(3, 'y'); + + String s = new String(charArr); + assertEquals("Ahoy", s); + } + + @Test + public void boolOp() { + boolArr = new boolean[]{true, false}; + + interop.boolArr().set(1, !interop.boolArr().get(1)); + + assertEquals(boolArr[0], boolArr[1]); + } + + @Test + public void byteSum() { + byteArr = new byte[]{(byte) 1, (byte) 2, (byte) 3}; + assertSum("Sum is OK", 6, interop.byteArr()); + } + + @Test + public void shortSum() { + shortArr = new short[]{(short) 1, (short) 2, (short) 3}; + assertSum("Sum is OK", 6, interop.shortArr()); + } + + @Test + public void intSum() { + intArr = new int[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.intArr()); + } + + @Test + public void longSum() { + longArr = new long[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.longArr()); + } + + @Test + public void floatSum() { + floatArr = new float[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.floatArr()); + } + + @Test + public void doubleSum() { + doubleArr = new double[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.doubleArr()); + } + + @Test + public void writeSomebyteSum() { + byteArr = new byte[]{(byte) 10, (byte) 2, (byte) 3}; + interop.byteArr().set(0, (byte) 1); + assertSum("Sum is OK", 6, interop.byteArr()); + } + + @Test + public void writeSomeshortSum() { + shortArr = new short[]{(short) 10, (short) 2, (short) 3}; + interop.shortArr().set(0, (short) 1); + assertSum("Sum is OK", 6, interop.shortArr()); + } + + @Test + public void writeSomeintSum() { + intArr = new int[]{10, 2, 3}; + interop.intArr().set(0, 1); + assertSum("Sum is OK", 6, interop.intArr()); + } + + @Test + public void writeSomelongSum() { + longArr = new long[]{10, 2, 3}; + interop.longArr().set(0, (long) 1); + assertSum("Sum is OK", 6, interop.longArr()); + } + + @Test + public void writeSomefloatSum() { + floatArr = new float[]{10, 2, 3}; + interop.floatArr().set(0, (float) 1); + assertSum("Sum is OK", 6, interop.floatArr()); + } + + @Test + public void writeSomedoubleSum() { + doubleArr = new double[]{10, 2, 3}; + interop.doubleArr().set(0, (double) 1); + assertSum("Sum is OK", 6, interop.doubleArr()); + } + + private static void assertSum(String msg, double expected, List numbers) { + double v = 0.0; + for (Number n : numbers) { + v += n.doubleValue(); + } + assertEquals(msg, expected, v, 0.05); + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java.test; + +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrimitiveRawArrayInteropTest { + private Object[] objArr; + private byte[] byteArr; + private short[] shortArr; + private int[] intArr; + private long[] longArr; + private float[] floatArr; + private double[] doubleArr; + private char[] charArr; + private boolean[] boolArr; + + public Object arr(int type) { + switch (type) { + case 0: + return objArr; + case 1: + return byteArr; + case 2: + return shortArr; + case 3: + return intArr; + case 4: + return longArr; + case 5: + return floatArr; + case 6: + return doubleArr; + case 7: + return charArr; + case 8: + return boolArr; + default: + throw new IllegalStateException("type: " + type); + } + } + + public interface RawInterop { + List arr(int type); + } + + private TruffleObject obj; + private RawInterop interop; + + @Before + public void initObjects() { + obj = JavaInterop.asTruffleObject(this); + interop = JavaInterop.asJavaObject(RawInterop.class, obj); + } + + @Test + public void everyThingIsNull() { + assertNull(interop.arr(0)); + assertNull(interop.arr(1)); + assertNull(interop.arr(2)); + assertNull(interop.arr(3)); + assertNull(interop.arr(4)); + assertNull(interop.arr(5)); + assertNull(interop.arr(6)); + assertNull(interop.arr(7)); + assertNull(interop.arr(8)); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + public void stringAsList() { + objArr = new Object[]{"Hello", "World", "!"}; + List list = interop.arr(0); + assertEquals("Three elements", 3, list.size()); + assertEquals("Hello", list.get(0)); + assertEquals("World", list.get(1)); + assertEquals("!", list.get(2)); + + list.set(1, "there"); + assertEquals("there", objArr[1]); + + list.set(0, null); + assertNull("set to null", objArr[0]); + + List rawList = list; + rawList.set(0, 42); + assertEquals("safelly changed", 42, objArr[0]); + } + + @Test + public void charOp() { + charArr = new char[]{'A', 'h', 'o', 'j'}; + assertEquals('j', (char) interop.arr(7).get(3)); + interop.arr(7).set(3, 'y'); + + String s = new String(charArr); + assertEquals("Ahoy", s); + } + + @Test + public void boolOp() { + boolArr = new boolean[]{true, false}; + + interop.arr(8).set(1, !(Boolean) interop.arr(8).get(1)); + + assertEquals(boolArr[0], boolArr[1]); + } + + @Test + public void byteSum() { + byteArr = new byte[]{(byte) 1, (byte) 2, (byte) 3}; + assertSum("Sum is OK", 6, interop.arr(1)); + } + + @Test + public void shortSum() { + shortArr = new short[]{(short) 1, (short) 2, (short) 3}; + assertSum("Sum is OK", 6, interop.arr(2)); + } + + @Test + public void intSum() { + intArr = new int[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.arr(3)); + } + + @Test + public void longSum() { + longArr = new long[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.arr(4)); + } + + @Test + public void floatSum() { + floatArr = new float[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.arr(5)); + } + + @Test + public void doubleSum() { + doubleArr = new double[]{1, 2, 3}; + assertSum("Sum is OK", 6, interop.arr(6)); + } + + @Test + public void writeSomebyteSum() { + byteArr = new byte[]{(byte) 10, (byte) 2, (byte) 3}; + interop.arr(1).set(0, (byte) 1); + assertSum("Sum is OK", 6, interop.arr(1)); + } + + @Test + public void writeSomeshortSum() { + shortArr = new short[]{(short) 10, (short) 2, (short) 3}; + interop.arr(2).set(0, (short) 1); + assertSum("Sum is OK", 6, interop.arr(2)); + } + + @Test + public void writeSomeintSum() { + intArr = new int[]{10, 2, 3}; + interop.arr(3).set(0, 1); + assertSum("Sum is OK", 6, interop.arr(3)); + } + + @Test + public void writeSomelongSum() { + longArr = new long[]{10, 2, 3}; + interop.arr(4).set(0, (long) 1); + assertSum("Sum is OK", 6, interop.arr(4)); + } + + @Test + public void writeSomefloatSum() { + floatArr = new float[]{10, 2, 3}; + interop.arr(5).set(0, (float) 1); + assertSum("Sum is OK", 6, interop.arr(5)); + } + + @Test + public void writeSomedoubleSum() { + doubleArr = new double[]{10, 2, 3}; + interop.arr(6).set(0, (double) 1); + assertSum("Sum is OK", 6, interop.arr(6)); + } + + private static void assertSum(String msg, double expected, List numbers) { + double v = 0.0; + for (Object o : numbers) { + if (o instanceof Number) { + Number n = (Number) o; + v += n.doubleValue(); + } + } + assertEquals(msg, expected, v, 0.05); + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ArrayGetSizeNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ArrayGetSizeNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.Array; + +final class ArrayGetSizeNode extends RootNode { + ArrayGetSizeNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + JavaInterop.JavaObject receiver = (JavaInterop.JavaObject) ForeignAccess.getReceiver(frame); + Object obj = receiver.obj; + if (obj == null) { + return 0; + } + return Array.getLength(obj); + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ArrayHasSizeNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ArrayHasSizeNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.Array; + +final class ArrayHasSizeNode extends RootNode { + ArrayHasSizeNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + JavaInterop.JavaObject receiver = (JavaInterop.JavaObject) ForeignAccess.getReceiver(frame); + Object obj = receiver.obj; + if (obj == null) { + return false; + } + try { + return obj instanceof Object[] || Array.getLength(obj) >= 0; + } catch (IllegalArgumentException ex) { + return Boolean.FALSE; + } + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaFunctionForeignAccess.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaFunctionForeignAccess.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.nodes.RootNode; + +final class JavaFunctionForeignAccess implements ForeignAccess.Factory10 { + @Override + public CallTarget accessIsNull() { + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(Boolean.FALSE)); + } + + @Override + public CallTarget accessIsExecutable() { + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(Boolean.TRUE)); + } + + @Override + public CallTarget accessIsBoxed() { + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(Boolean.FALSE)); + } + + @Override + public CallTarget accessHasSize() { + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(Boolean.FALSE)); + } + + @Override + public CallTarget accessGetSize() { + return null; + } + + @Override + public CallTarget accessUnbox() { + return null; + } + + @Override + public CallTarget accessRead() { + return null; + } + + @Override + public CallTarget accessWrite() { + return null; + } + + @Override + public CallTarget accessExecute(int argumentsLength) { + return Truffle.getRuntime().createCallTarget(new JavaFunctionNode()); + } + + @Override + public CallTarget accessInvoke(int argumentsLength) { + return null; + } + + @Override + public CallTarget accessMessage(Message unknown) { + return null; + } + + @Override + public CallTarget accessNew(int argumentsLength) { + return null; + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaFunctionNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaFunctionNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +final class JavaFunctionNode extends RootNode { + JavaFunctionNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + JavaInterop.JavaFunctionObject receiver = (JavaInterop.JavaFunctionObject) ForeignAccess.getReceiver(frame); + List args = ForeignAccess.getArguments(frame); + return execute(receiver, args.toArray()); + } + + @SuppressWarnings("paramAssign") + static Object execute(JavaInterop.JavaFunctionObject receiver, Object[] args) { + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof JavaInterop.JavaObject) { + args[i] = ((JavaInterop.JavaObject) args[i]).obj; + } + } + try { + Object ret = receiver.method.invoke(receiver.obj, args); + if (JavaInterop.isPrimitive(ret)) { + return ret; + } + return JavaInterop.asTruffleObject(ret); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new IllegalStateException(ex); + } + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInterop.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Helper methods to simplify access to objects of {@link TruffleLanguage Truffle languages} from + * Java and the other way around. The Java/Truffle interop builds on + * {@link ForeignAccess mutual interoperability} between individual Truffle languages - it + * just encapsulates it into Java facade to make it as natural to access foreign + * {@link TruffleObject Truffle objects} as Java programmers are used to when accessing + * Java objects and interfaces directly. + */ +public final class JavaInterop { + static final Object[] EMPTY = {}; + + private JavaInterop() { + } + + /** + * Wraps a {@link TruffleObject foreign object} into easy to use interface. Imagine one wants to + * access a JavaScript object like: + * + *
+     * var obj = {
+     *   'x' : 10,
+     *   'y' : 3.3,
+     *   'name' : 'Truffle'
+     * };
+     * 
+ * + * from Java. One can do it by defining an interface: + * + *
+     * interface ObjAccess {
+     *   int x();
+     *   {@link MethodMessage @MethodMessage}(message = "WRITE")
+     *   void x(int newValue);
+     *   double y();
+     *   String name();
+     * }
+     * 
+ * + * and obtaining its instance by calling this conversion method: + * + *
+     * ObjAccess access = JavaInterop.{@link #asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) asJavaObject}(ObjAccess.class, obj);
+     * assert access.x() == 10 : "Still the default";
+     * access.x(5);
+     * assert access.x() == 5 : "Changed to five";
+     * 
+ * + * @param type of requested and returned value + * @param type interface modeling structure of foreignObject in Java + * @param foreignObject object coming from a {@link TruffleObject Truffle language} + * @return instance of requested interface granting access to specified + * foreignObject + */ + public static T asJavaObject(Class type, TruffleObject foreignObject) { + if (type.isInstance(foreignObject)) { + return type.cast(foreignObject); + } else { + if (!type.isInterface()) { + throw new IllegalArgumentException(); + } + Object obj = Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new TruffleHandler(foreignObject)); + return type.cast(obj); + } + } + + /** + * Exports a Java object for use in any {@link TruffleLanguage}. The system scans structure of + * provided object and exposes all public fields and methods to any Truffle + * language. An instance of class + * + *
+     * class JavaRecord {
+     *   public int x;
+     *   public double y;
+     *   public String name() {
+     *     return "Truffle";
+     *   }
+     * }
+     * {@link TruffleObject} obj = JavaInterop.asTruffleObject(new JavaRecord());
+     * 
+ * + * can then be access from JavaScript or any other Truffle based language as + * + *
+     * obj.x;
+     * obj.y;
+     * obj.name();
+     * 
+ * + * When the obj represents a {@link Class}, then the created {@link TruffleObject} + * will allow access to public and static fields and methods from the class. + * + * @param obj a Java object to convert into one suitable for Truffle languages + * @return converted object + */ + public static TruffleObject asTruffleObject(Object obj) { + if (obj instanceof TruffleObject) { + return ((TruffleObject) obj); + } + if (obj instanceof Class) { + return new JavaObject(null, (Class) obj); + } + if (obj == null) { + return JavaObject.NULL; + } + return new JavaObject(obj, obj.getClass()); + } + + /** + * Takes executable object from a {@link TruffleLanguage} and converts it into an instance of a + * Java functional interface. + * + * @param requested and returned type + * @param functionalType interface with a single defined method - so called + * functional interface + * @param function Truffle that responds to {@link Message#IS_EXECUTABLE} and can be + * invoked + * @return instance of interface that wraps the provided function + */ + public static T asJavaFunction(Class functionalType, TruffleObject function) { + final Method[] arr = functionalType.getDeclaredMethods(); + if (!functionalType.isInterface() || arr.length != 1) { + throw new IllegalArgumentException(); + } + Object obj = Proxy.newProxyInstance(functionalType.getClassLoader(), new Class[]{functionalType}, new SingleHandler(function)); + return functionalType.cast(obj); + } + + /** + * Takes a functional interface and its implementation (for example lambda function) and + * converts it into object executable by Truffle languages. Here is a definition of + * function returning the meaning of life as lambda expression, converting it back to + * Java and using it: + * + *
+     * TruffleObject to = JavaInterop.asTruffleFunction(Callable.class, () -> 42);
+     * Callable c = JavaInterop.{@link #asJavaFunction(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) asJavaFunction}(Callable.class, to);
+     * assert c.call() == 42;
+     * 
+ * + * @param requested interface and implementation + * @param functionalType interface with a single defined method - so called + * functional interface + * @param implementation implementation of the interface, or directly a lambda expression + * defining the required behavior + * @return an {@link Message#IS_EXECUTABLE executable} {@link TruffleObject} ready to be used in + * any Truffle language + */ + public static TruffleObject asTruffleFunction(Class functionalType, T implementation) { + final Method[] arr = functionalType.getDeclaredMethods(); + if (!functionalType.isInterface() || arr.length != 1) { + throw new IllegalArgumentException(); + } + return new JavaFunctionObject(arr[0], implementation); + } + + static Message findMessage(MethodMessage mm) { + if (mm == null) { + return null; + } + return Message.valueOf(mm.message()); + } + + static Object toJava(Object ret, Method method) { + if (isPrimitive(ret)) { + return ret; + } + 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; + } + if (ret instanceof TruffleObject) { + final TruffleObject truffleObject = (TruffleObject) ret; + if (retType.isInterface()) { + if (method.getReturnType() == List.class && Boolean.TRUE.equals(message(Message.HAS_SIZE, truffleObject))) { + Class elementType = Object.class; + Type type = method.getGenericReturnType(); + if (type instanceof ParameterizedType) { + ParameterizedType parametrizedType = (ParameterizedType) type; + final Type[] arr = parametrizedType.getActualTypeArguments(); + if (arr.length == 1 && arr[0] instanceof Class) { + elementType = (Class) arr[0]; + } + } + return TruffleList.create(elementType, truffleObject); + } + return asJavaObject(retType, truffleObject); + } + } + return ret; + } + + private static final class SingleHandler implements InvocationHandler { + private final TruffleObject symbol; + private CallTarget target; + + public SingleHandler(TruffleObject obj) { + this.symbol = obj; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { + Object[] args = arguments == null ? EMPTY : arguments; + if (target == null) { + Node executeMain = Message.createExecute(args.length).createNode(); + RootNode symbolNode = new TemporaryRoot(TruffleLanguage.class, executeMain, symbol); + target = Truffle.getRuntime().createCallTarget(symbolNode); + } + Object ret = target.call(args); + return toJava(ret, method); + } + } + + private static final class TruffleHandler implements InvocationHandler { + private final TruffleObject obj; + + public TruffleHandler(TruffleObject obj) { + this.obj = obj; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { + Object[] args = arguments == null ? EMPTY : arguments; + Object val; + for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + continue; + } + if (Proxy.isProxyClass(args[i].getClass())) { + InvocationHandler h = Proxy.getInvocationHandler(args[i]); + if (h instanceof TruffleHandler) { + args[i] = ((TruffleHandler) h).obj; + } + } + } + + if (Object.class == method.getDeclaringClass()) { + return method.invoke(obj, args); + } + + String name = method.getName(); + Message message = findMessage(method.getAnnotation(MethodMessage.class)); + if (message == Message.WRITE) { + if (args == null || args.length != 1) { + throw new IllegalStateException("Method needs to have a single argument to handle WRITE message " + method); + } + message(Message.WRITE, obj, name, args[0]); + return null; + } + if (message == Message.HAS_SIZE || message == Message.IS_BOXED || message == Message.IS_EXECUTABLE || message == Message.IS_NULL || message == Message.GET_SIZE) { + return message(message, obj); + } + + if (message == Message.READ) { + val = message(Message.READ, obj, name); + return toJava(val, method); + } + + if (message == Message.UNBOX) { + val = message(Message.UNBOX, obj); + return toJava(val, method); + } + + if (Message.createExecute(0).equals(message)) { + List copy = new ArrayList<>(args.length + 1); + // copy.add(obj); + copy.addAll(Arrays.asList(args)); + message = Message.createExecute(copy.size()); + val = message(message, obj, copy.toArray()); + return toJava(val, method); + } + + if (Message.createInvoke(0).equals(message)) { + List copy = new ArrayList<>(args.length + 1); + copy.add(obj); + copy.addAll(Arrays.asList(args)); + message = Message.createInvoke(copy.size()); + val = message(message, obj, copy.toArray()); + return toJava(val, method); + } + + if (Message.createNew(0).equals(message)) { + message = Message.createNew(args.length); + val = message(message, obj, args); + return toJava(val, method); + } + + if (message == null) { + val = message(Message.READ, obj, name); + if (isPrimitive(val)) { + return val; + } + TruffleObject attr = (TruffleObject) val; + if (Boolean.FALSE.equals(message(Message.IS_EXECUTABLE, attr))) { + if (args.length == 0) { + return toJava(attr, method); + } + throw new IllegalStateException(attr + " cannot be invoked with " + args.length + " parameters"); + } + List callArgs = new ArrayList<>(args.length + 1); + // callArgs.add(attr); + callArgs.addAll(Arrays.asList(args)); + Object ret = message(Message.createExecute(callArgs.size()), attr, callArgs.toArray()); + return toJava(ret, method); + } + throw new IllegalStateException("Unknown message: " + message); + } + + } + + static boolean isPrimitive(Object attr) { + if (attr instanceof TruffleObject) { + return false; + } + if (attr instanceof Number) { + return true; + } + if (attr instanceof String) { + return true; + } + if (attr instanceof Character) { + return true; + } + if (attr instanceof Boolean) { + return true; + } + return false; + } + + static Object message(final Message m, Object receiver, Object... arr) { + Node n = m.createNode(); + CallTarget callTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(TruffleLanguage.class, n, (TruffleObject) receiver)); + return callTarget.call(arr); + } + + private static class TemporaryRoot extends RootNode { + @Node.Child private Node foreignAccess; + private final TruffleObject function; + + @SuppressWarnings("rawtypes") + public TemporaryRoot(Class lang, Node foreignAccess, TruffleObject function) { + super(lang, null, null); + this.foreignAccess = foreignAccess; + this.function = function; + } + + @Override + public Object execute(VirtualFrame frame) { + return ForeignAccess.execute(foreignAccess, frame, function, frame.getArguments()); + } + } // end of TemporaryRoot + + static final class JavaFunctionObject implements TruffleObject { + final Method method; + final Object obj; + + public JavaFunctionObject(Method method, Object obj) { + this.method = method; + this.obj = obj; + } + + @Override + public ForeignAccess getForeignAccess() { + return ForeignAccess.create(JavaFunctionObject.class, new JavaFunctionForeignAccess()); + } + + } // end of JavaFunctionObject + + static final class JavaObject implements TruffleObject { + static final JavaObject NULL = new JavaObject(null, Object.class); + + final Object obj; + final Class clazz; + + public JavaObject(Object obj, Class clazz) { + this.obj = obj; + this.clazz = clazz; + } + + @Override + public ForeignAccess getForeignAccess() { + return ForeignAccess.create(JavaObject.class, new JavaObjectForeignAccess()); + } + + @Override + public int hashCode() { + return System.identityHashCode(obj); + } + + @Override + public boolean equals(Object other) { + if (other instanceof JavaObject) { + return obj == ((JavaObject) other).obj && clazz == ((JavaObject) other).clazz; + } + return false; + } + } // end of JavaObject + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInteropLanguage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaInteropLanguage.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.TruffleLanguage; + +abstract class JavaInteropLanguage extends TruffleLanguage { +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaNewNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaNewNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +final class JavaNewNode extends RootNode { + JavaNewNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + JavaInterop.JavaObject receiver = (JavaInterop.JavaObject) ForeignAccess.getReceiver(frame); + List args = ForeignAccess.getArguments(frame); + return execute(receiver, args.toArray()); + } + + static Object execute(JavaInterop.JavaObject receiver, Object[] args) { + if (receiver.obj != null) { + throw new IllegalStateException("Can only work on classes: " + receiver.obj); + } + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof JavaInterop.JavaObject) { + args[i] = ((JavaInterop.JavaObject) args[i]).obj; + } + } + IllegalStateException ex = new IllegalStateException("No suitable constructor found for " + receiver.clazz); + for (Constructor constructor : receiver.clazz.getConstructors()) { + try { + Object ret = constructor.newInstance(args); + if (JavaInterop.isPrimitive(ret)) { + return ret; + } + return JavaInterop.asTruffleObject(ret); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException instEx) { + ex = new IllegalStateException(instEx); + } + } + throw ex; + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaObjectForeignAccess.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaObjectForeignAccess.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.nodes.RootNode; + +final class JavaObjectForeignAccess implements ForeignAccess.Factory10 { + @Override + public CallTarget accessIsNull() { + return Truffle.getRuntime().createCallTarget(new NullCheckNode()); + } + + @Override + public CallTarget accessIsExecutable() { + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(Boolean.FALSE)); + } + + @Override + public CallTarget accessIsBoxed() { + return null; + } + + @Override + public CallTarget accessHasSize() { + return Truffle.getRuntime().createCallTarget(new ArrayHasSizeNode()); + } + + @Override + public CallTarget accessGetSize() { + return Truffle.getRuntime().createCallTarget(new ArrayGetSizeNode()); + } + + @Override + public CallTarget accessUnbox() { + return null; + } + + @Override + public CallTarget accessRead() { + return Truffle.getRuntime().createCallTarget(new ReadFieldNode()); + } + + @Override + public CallTarget accessWrite() { + return Truffle.getRuntime().createCallTarget(new WriteFieldNode()); + } + + @Override + public CallTarget accessExecute(int argumentsLength) { + return null; + } + + @Override + public CallTarget accessInvoke(int argumentsLength) { + return Truffle.getRuntime().createCallTarget(new JavaObjectMethodNode()); + } + + @Override + public CallTarget accessNew(int argumentsLength) { + return Truffle.getRuntime().createCallTarget(new JavaNewNode()); + } + + @Override + public CallTarget accessMessage(Message unknown) { + return null; + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaObjectMethodNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/JavaObjectMethodNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.RootNode; +import java.util.List; + +final class JavaObjectMethodNode extends RootNode { + @Child ReadFieldNode readField; + @Child JavaFunctionNode function; + + JavaObjectMethodNode() { + super(JavaInteropLanguage.class, null, null); + readField = new ReadFieldNode(); + function = new JavaFunctionNode(); + } + + @Override + public Object execute(VirtualFrame frame) { + JavaInterop.JavaFunctionObject f = (JavaInterop.JavaFunctionObject) readField.execute(frame); + List args = ForeignAccess.getArguments(frame); + return JavaFunctionNode.execute(f, args.subList(1, args.size()).toArray()); + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/MethodMessage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/MethodMessage.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to obtain fine grain control over behavior of + * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)} + * wrapper interfaces. The interface created by + * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)} + * method implements its methods by sending {@link Message messsages} to {@link TruffleObject} + * provided at the time of construction. There is a default sequence of operations for each method, + * which is good enough to read fields or invoke methods. However the + * {@link com.oracle.truffle.api.interop Interop API} is far richer and supports additional + * {@link Message messages} (not only the well known ones, but also arbitrary custom ones). To + * control which {@link Message} is sent one can annotate each method by this annotation.
+ * Writing to a field
For example to write to field x of a JSON object: + * + *
+ * var obj = { 'x' : 5 }
+ * 
+ * + * one can define the appropriate wrapper interface as: + * + *
+ * interface ObjInterop {
+ *   {@link MethodMessage @MethodMessage}(message = "WRITE")
+ *   void x(int value);
+ * }
+ * 
+ * + * Then one can change the value of field x in obj from Java by calling: + * + *
+ * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) JavaInterop.asJavaObject(ObjInterop.class, obj).x(10);
+ * 
+ * + * the value of the x field is going to be 10 then. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MethodMessage { + /** + * Identification of the {@link Message message} to send. Well known messages include fields of + * the {@link Message} class (e.g. "READ", "WRITE", "UNBOX", + * IS_NULL) or slightly mangled names of {@link Message} class factory methods ( + * EXECUTE, INVOKE). For more details on the string encoding of message names + * see {@link Message#valueOf(java.lang.String)} method. + * + * @return string identification of an inter-operability message + * @see Message#valueOf(java.lang.String) + */ + String message(); +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/NullCheckNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/NullCheckNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.java.JavaInterop.JavaObject; +import com.oracle.truffle.api.nodes.RootNode; + +final class NullCheckNode extends RootNode { + NullCheckNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + return ForeignAccess.getReceiver(frame) == JavaObject.NULL; + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ReadArgNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ReadArgNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.Node; + +class ReadArgNode extends Node { + private final int argIndex; + + public ReadArgNode(int argIndex) { + this.argIndex = argIndex; + } + + public Object execute(VirtualFrame frame) { + return ForeignAccess.getArguments(frame).get(argIndex); + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ReadFieldNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ReadFieldNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +final class ReadFieldNode extends RootNode { + ReadFieldNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + JavaInterop.JavaObject receiver = (JavaInterop.JavaObject) ForeignAccess.getReceiver(frame); + Object obj = receiver.obj; + final boolean onlyStatic = obj == null; + final Object nameOrIndex = ForeignAccess.getArguments(frame).get(0); + Object val; + if (nameOrIndex instanceof Integer) { + val = Array.get(obj, (int) nameOrIndex); + } else { + String name = (String) nameOrIndex; + try { + final Field field = receiver.clazz.getField(name); + final boolean isStatic = (field.getModifiers() & Modifier.STATIC) != 0; + if (onlyStatic != isStatic) { + throw new NoSuchFieldException(); + } + val = field.get(obj); + } catch (NoSuchFieldException ex) { + for (Method m : receiver.clazz.getMethods()) { + final boolean isStatic = (m.getModifiers() & Modifier.STATIC) != 0; + if (onlyStatic != isStatic) { + continue; + } + if (m.getName().equals(name)) { + return new JavaInterop.JavaFunctionObject(m, obj); + } + } + throw (NoSuchFieldError) new NoSuchFieldError(ex.getMessage()).initCause(ex); + } + } + if (JavaInterop.isPrimitive(val)) { + return val; + } + return JavaInterop.asTruffleObject(val); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ReadReceiverNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/ReadReceiverNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.nodes.Node; + +class ReadReceiverNode extends Node { + + public Object execute(VirtualFrame frame) { + return ForeignAccess.getReceiver(frame); + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/TruffleList.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/TruffleList.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import java.util.AbstractList; +import java.util.List; + +final class TruffleList extends AbstractList { + private final TruffleObject array; + private final Class type; + + private TruffleList(Class elementType, TruffleObject array) { + this.array = array; + this.type = elementType; + } + + public static List create(Class elementType, TruffleObject array) { + return new TruffleList<>(elementType, array); + } + + @Override + public T get(int index) { + return type.cast(JavaInterop.message(Message.READ, array, index)); + } + + @Override + public T set(int index, T element) { + T prev = get(index); + JavaInterop.message(Message.WRITE, array, index, element); + return prev; + } + + @Override + public int size() { + return (Integer) JavaInterop.message(Message.GET_SIZE, array); + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/UnboxNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/UnboxNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.NodeChild; +import com.oracle.truffle.api.dsl.NodeChildren; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.nodes.Node; + +@NodeChildren(value = {@NodeChild(value = "valueNode", type = ReadArgNode.class)}) +abstract class UnboxNode extends Node { + @Node.Child private Node unbox; + @Node.Child private Node isBoxed; + + public abstract Object executeUnbox(VirtualFrame frame); + + @Specialization + public int executeUnbox(int value) { + return value; + } + + @Specialization + public long executeUnbox(long value) { + return value; + } + + @Specialization + public String executeUnbox(String value) { + return value; + } + + @Specialization(guards = "isBoxedPrimitive(frame, foreignValue)") + public Object executeUnbox(VirtualFrame frame, TruffleObject foreignValue) { + if (unbox == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + unbox = insert(Message.UNBOX.createNode()); + } + return ForeignAccess.execute(unbox, frame, foreignValue); + } + + protected final boolean isBoxedPrimitive(VirtualFrame frame, TruffleObject object) { + if (isBoxed == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + isBoxed = insert(Message.IS_BOXED.createNode()); + } + return (boolean) ForeignAccess.execute(isBoxed, frame, object); + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/WriteFieldNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/WriteFieldNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.interop.java; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.java.JavaInterop.JavaObject; +import com.oracle.truffle.api.nodes.RootNode; +import java.lang.reflect.Array; + +class WriteFieldNode extends RootNode { + + public WriteFieldNode() { + super(JavaInteropLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + JavaInterop.JavaObject receiver = (JavaInterop.JavaObject) ForeignAccess.getReceiver(frame); + Object obj = receiver.obj; + final Object indexOrName = ForeignAccess.getArguments(frame).get(0); + Object value = ForeignAccess.getArguments(frame).get(1); + if (indexOrName instanceof Integer) { + Array.set(obj, (Integer) indexOrName, value); + return JavaObject.NULL; + } + String name = (String) indexOrName; + try { + receiver.clazz.getField(name).set(obj, value); + return JavaObject.NULL; + } catch (NoSuchFieldException ex) { + throw new RuntimeException(ex); + } + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java/src/com/oracle/truffle/api/interop/java/package-info.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @ApiInfo( + group="Stable" + ) + */ + +/** + * {@link com.oracle.truffle.api.interop.java.JavaInterop Helper methods} + * to simplify co-operation of Java and + * {@link com.oracle.truffle.api.interop.TruffleObject Truffle objects}. + */ +package com.oracle.truffle.api.interop.java; \ No newline at end of file diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Execute.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Execute.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Execute.java Mon Sep 07 17:07:20 2015 +0200 @@ -60,15 +60,14 @@ return type; } - @Override - public String toString() { + String name() { switch (type) { case EXECUTE: - return "msgExecute"; + return "EXECUTE"; case INVOKE: - return "msgInvoke"; + return "INVOKE"; default: - return "msgNew"; + return "NEW"; } } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignAccess.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignAccess.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignAccess.java Mon Sep 07 17:07:20 2015 +0200 @@ -78,7 +78,7 @@ /** * Executes {@link Message#createNode() foreign node}. - * + * * @param foreignNode the createNode created by {@link Message#createNode()} * @param frame the call frame * @param receiver foreign object to receive the message passed to {@link Message#createNode()} @@ -189,7 +189,7 @@ /** * Handles {@link Message#IS_EXECUTABLE} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -197,7 +197,7 @@ /** * Handles {@link Message#IS_BOXED} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -205,7 +205,7 @@ /** * Handles {@link Message#HAS_SIZE} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -213,7 +213,7 @@ /** * Handles {@link Message#GET_SIZE} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -221,7 +221,7 @@ /** * Handles {@link Message#UNBOX} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -229,7 +229,7 @@ /** * Handles {@link Message#READ} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -237,7 +237,7 @@ /** * Handles {@link Message#WRITE} message. - * + * * @return call target to handle the message or null if this message is not * supported */ @@ -245,7 +245,7 @@ /** * Handles {@link Message#createExecute(int)} messages. - * + * * @param argumentsLength number of parameters the messages has been created for * @return call target to handle the message or null if this message is not * supported @@ -254,7 +254,7 @@ /** * Handles {@link Message#createInvoke(int)} messages. - * + * * @param argumentsLength number of parameters the messages has been created for * @return call target to handle the message or null if this message is not * supported @@ -263,7 +263,7 @@ /** * Handles {@link Message#createNew(int)} messages. - * + * * @param argumentsLength number of parameters the messages has been created for * @return call target to handle the message or null if this message is not * supported diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignObjectAccessHeadNode.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignObjectAccessHeadNode.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/ForeignObjectAccessHeadNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -48,7 +48,25 @@ } public Object executeForeign(VirtualFrame frame, TruffleObject receiver, Object... arguments) { - return first.executeWith(frame, receiver, arguments); + Object ret = first.executeWith(frame, receiver, arguments); + assert assertReturnValue(ret) : "Only primitive values or TruffleObject expected: " + ret; + return ret; + } + + private static boolean assertReturnValue(Object ret) { + if (ret instanceof Number) { + return true; + } + if (ret instanceof String) { + return true; + } + if (ret instanceof Character) { + return true; + } + if (ret instanceof Boolean) { + return true; + } + return ret instanceof TruffleObject; } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/GetSize.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/GetSize.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/GetSize.java Mon Sep 07 17:07:20 2015 +0200 @@ -30,11 +30,6 @@ static Message INSTANCE = new GetSize(); @Override - public String toString() { - return "msgGetSize"; - } - - @Override public int hashCode() { return HASH; } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HasSize.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HasSize.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HasSize.java Mon Sep 07 17:07:20 2015 +0200 @@ -32,9 +32,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgHasSize"; - } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsBoxed.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsBoxed.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsBoxed.java Mon Sep 07 17:07:20 2015 +0200 @@ -32,9 +32,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgIsBoxed"; - } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsExecutable.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsExecutable.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsExecutable.java Mon Sep 07 17:07:20 2015 +0200 @@ -32,9 +32,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgIsExecutable"; - } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsNull.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsNull.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/IsNull.java Mon Sep 07 17:07:20 2015 +0200 @@ -32,9 +32,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgIsNull"; - } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/KnownMessage.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/KnownMessage.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/KnownMessage.java Mon Sep 07 17:07:20 2015 +0200 @@ -28,4 +28,8 @@ * Marker class. */ abstract class KnownMessage extends Message { + @Override + public final String toString() { + return toString(this); + } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Message.java Mon Sep 07 17:07:20 2015 +0200 @@ -178,4 +178,68 @@ return new ForeignObjectAccessHeadNode(this); } + /** + * Converts the message into canonical string representation. The converted string can be + * stored, persisted, transfered and later passed to {@link #valueOf(java.lang.String)} to + * construct the message again. + * + * @param message the message to convert + * @return canonical string representation + */ + public static String toString(Message message) { + if (Message.READ == message) { + return "READ"; // NOI18N + } + if (Message.WRITE == message) { + return "WRITE"; // NOI18N + } + if (Message.UNBOX == message) { + return "UNBOX"; // NOI18N + } + if (Message.GET_SIZE == message) { + return "GET_SIZE"; // NOI18N + } + if (Message.HAS_SIZE == message) { + return "HAS_SIZE"; // NOI18N + } + if (Message.IS_NULL == message) { + return "IS_NULL"; // NOI18N + } + if (Message.IS_BOXED == message) { + return "IS_BOXED"; // NOI18N + } + if (Message.IS_EXECUTABLE == message) { + return "IS_EXECUTABLE"; // NOI18N + } + if (message instanceof Execute) { + return ((Execute) message).name(); + } + return message.getClass().getName(); + } + + /** + * Converts string representation into real message. If the string was obtained by + * {@link #toString(com.oracle.truffle.api.interop.Message)} method, it is guaranteed to be + * successfully recognized (if the classpath of the system remains the same). + * + * @param message canonical string representation of a message + * @return the message + * @throws IllegalArgumentException if the string does not represent known message + */ + public static Message valueOf(String message) { + try { + return (Message) Message.class.getField(message).get(null); + } catch (Exception ex) { + try { + String factory = "create" + message.charAt(0) + message.substring(1).toLowerCase(); + return (Message) Message.class.getMethod(factory, int.class).invoke(null, 0); + } catch (Exception ex2) { + try { + return (Message) Class.forName(message).newInstance(); + } catch (Exception ex1) { + throw new IllegalArgumentException("Cannot find message for " + message, ex); + } + } + } + } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Read.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Read.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Read.java Mon Sep 07 17:07:20 2015 +0200 @@ -40,9 +40,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgRead"; - } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/UnaryMessage.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/UnaryMessage.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/UnaryMessage.java Mon Sep 07 17:07:20 2015 +0200 @@ -38,7 +38,4 @@ @Override public abstract int hashCode(); - - @Override - public abstract String toString(); } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Unbox.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Unbox.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Unbox.java Mon Sep 07 17:07:20 2015 +0200 @@ -32,9 +32,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgUnbox"; - } } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Write.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Write.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/Write.java Mon Sep 07 17:07:20 2015 +0200 @@ -38,10 +38,4 @@ public int hashCode() { return HASH; } - - @Override - public String toString() { - return "msgWrite"; - } - } diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/impl/ReadOnlyArrayList.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/impl/ReadOnlyArrayList.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/impl/ReadOnlyArrayList.java Mon Sep 07 17:07:20 2015 +0200 @@ -40,6 +40,9 @@ this.arr = arr; this.first = first; this.last = last; + if (first > last) { + throw new IllegalArgumentException(); + } } public static List asList(T[] arr, int first, int last) { diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/MessageStringTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/MessageStringTest.java Mon Sep 07 17:07:20 2015 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.test.interop; + +import com.oracle.truffle.api.interop.Message; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Locale; +import static org.junit.Assert.*; +import org.junit.Test; + +public class MessageStringTest { + @Test + public void testFields() throws Exception { + for (Field f : Message.class.getFields()) { + if (f.getType() != Message.class) { + continue; + } + if ((f.getModifiers() & Modifier.STATIC) == 0) { + continue; + } + Message msg = (Message) f.get(null); + + String persistent = Message.toString(msg); + assertNotNull("Found name for " + f, persistent); + assertEquals("It is in upper case", persistent, persistent.toUpperCase(Locale.ENGLISH)); + + Message newMsg = Message.valueOf(persistent); + + assertSame("Same for " + f, msg, newMsg); + + assertEquals("Same toString()", persistent, msg.toString()); + } + } + + @Test + public void testFactoryMethods() throws Exception { + for (Method m : Message.class.getMethods()) { + if (m.getReturnType() != Message.class) { + continue; + } + if (!m.getName().startsWith("create")) { + continue; + } + if ((m.getModifiers() & Modifier.STATIC) == 0) { + continue; + } + Message msg = (Message) m.invoke(null, 0); + + String persistent = Message.toString(msg); + assertNotNull("Found name for " + m, persistent); + assertEquals("It is in upper case", persistent, persistent.toUpperCase(Locale.ENGLISH)); + + Message newMsg = Message.valueOf(persistent); + + assertEquals("Same for " + m, msg, newMsg); + + assertEquals("Same toString()", persistent, msg.toString()); + assertEquals("Same toString() for new one", persistent, newMsg.toString()); + } + } + + @Test + public void specialMessagePersitance() { + SpecialMsg msg = new SpecialMsg(); + String persistent = Message.toString(msg); + Message newMsg = Message.valueOf(persistent); + assertEquals("Message reconstructed", msg, newMsg); + } + + public static final class SpecialMsg extends Message { + + @Override + public boolean equals(Object message) { + return message instanceof SpecialMsg; + } + + @Override + public int hashCode() { + return 5425432; + } + } +} diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java Mon Sep 07 17:07:20 2015 +0200 @@ -40,36 +40,27 @@ } RootNode symbolNode; if ((symbol instanceof String) || (symbol instanceof Number) || (symbol instanceof Boolean) || (symbol instanceof Character)) { - symbolNode = new ConstantRootNode(type, symbol); + symbolNode = RootNode.createConstantNode(symbol); } else { Node executeMain = Message.createExecute(arr.length).createNode(); - symbolNode = new TemporaryRoot(type, executeMain, (TruffleObject) symbol, arr.length); + symbolNode = createTemporaryRoot(type, executeMain, (TruffleObject) symbol, arr.length); } return Truffle.getRuntime().createCallTarget(symbolNode); } - private static final class ConstantRootNode extends RootNode { - - private final Object value; - - public ConstantRootNode(Class> lang, Object value) { - super(lang, null, null); - this.value = value; - } - - @Override - public Object execute(VirtualFrame vf) { - return value; - } + @SuppressWarnings("rawtypes") + public static RootNode createTemporaryRoot(Class lang, Node foreignAccess, TruffleObject function, int argumentLength) { + return new TemporaryRoot(lang, foreignAccess, function, argumentLength); } - private static class TemporaryRoot extends RootNode { + static class TemporaryRoot extends RootNode { @Child private Node foreignAccess; @Child private ConvertNode convert; private final int argumentLength; private final TruffleObject function; - public TemporaryRoot(Class> lang, Node foreignAccess, TruffleObject function, int argumentLength) { + @SuppressWarnings("rawtypes") + public TemporaryRoot(Class lang, Node foreignAccess, TruffleObject function, int argumentLength) { super(lang, null, null); this.foreignAccess = foreignAccess; this.convert = new ConvertNode(); diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Mon Sep 07 17:07:20 2015 +0200 @@ -30,6 +30,8 @@ import com.oracle.truffle.api.debug.*; import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.source.*; import java.io.*; import java.net.*; @@ -601,6 +603,26 @@ } /** + * Obtains Java view of the object represented by this symbol. The method basically + * delegates to + * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)} + * just handles primitive types as well. + * + * @param the type of the view one wants to obtain + * @param representation the class of the view interface (it has to be an interface) + * @return instance of the view wrapping the object of this symbol + * @throws IOException in case it is not possible to obtain the value of the object + * @throws ClassCastException if the value cannot be converted to desired view + */ + public T as(Class representation) throws IOException { + Object obj = get(); + if (representation.isInstance(obj)) { + return representation.cast(obj); + } + return JavaInterop.asJavaObject(representation, (TruffleObject) obj); + } + + /** * Invokes the symbol. If the symbol represents a function, then it should be invoked with * provided arguments. If the symbol represents a field, then first argument (if provided) * should set the value to the field; the return value should be the actual value of the diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Fri Sep 04 16:41:38 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Mon Sep 07 17:07:20 2015 +0200 @@ -154,7 +154,7 @@ * stack) without prior knowledge of the language it has come from. * * Used for instance to determine the language of a RootNode: - * + * *
      * 
      * rootNode.getExecutionContext().getLanguageShortName();
@@ -194,4 +194,33 @@
      */
     public void applyInstrumentation() {
     }
+
+    /**
+     * Helper method to create a root node that always returns the same value. Certain operations
+     * (expecially {@link com.oracle.api.truffle.api.interop inter-operability} API) require return
+     * of stable {@link RootNode root nodes}. To simplify creation of such nodes, here is a factory
+     * method that can create {@link RootNode} that returns always the same value.
+     *
+     * @param constant the constant to return
+     * @return root node returning the constant
+     */
+    public static RootNode createConstantNode(Object constant) {
+        return new Constant(constant);
+    }
+
+    private static final class Constant extends RootNode {
+
+        private final Object value;
+
+        public Constant(Object value) {
+            super(TruffleLanguage.class, null, null);
+            this.value = value;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vf) {
+            return value;
+        }
+    }
+
 }
diff -r 025869c88840 -r e70b20f4bb00 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	Fri Sep 04 16:41:38 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Mon Sep 07 17:07:20 2015 +0200
@@ -80,7 +80,15 @@
                 "  defineFunction(\"function cnt() { return \" + n + \"; }\");\n" +
                 "  return n;\n" +
                 "}\n" +
-                "function null() {\n" +
+                "function returnsNull() {\n" +
+                "}\n" +
+                "function compoundObject() {\n" +
+                "  obj = new();\n" +
+                "  obj.fourtyTwo = fourtyTwo;\n" +
+                "  obj.plus = plus;\n" +
+                "  obj.returnsNull = returnsNull;\n" +
+                "  obj.returnsThis = obj;\n" +
+                "  return obj;\n" +
                 "}\n", "SL TCK"
             ).withMimeType("application/x-sl")
         );
@@ -105,7 +113,7 @@
 
     @Override
     protected String returnsNull() {
-        return "null";
+        return "returnsNull";
     }
 
     @Override
@@ -114,6 +122,11 @@
     }
 
     @Override
+    protected String compoundObject() {
+        return "compoundObject";
+    }
+
+    @Override
     protected String invalidCode() {
         // @formatter:off
         return
diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Fri Sep 04 16:41:38 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Mon Sep 07 17:07:20 2015 +0200
@@ -88,7 +88,7 @@
         this.functionRegistry = new SLFunctionRegistry();
         installBuiltins(installBuiltins);
 
-        this.emptyShape = LAYOUT.createShape(new ObjectType());
+        this.emptyShape = LAYOUT.createShape(new SLObjectType());
     }
 
     /**
diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java	Fri Sep 04 16:41:38 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java	Mon Sep 07 17:07:20 2015 +0200
@@ -74,6 +74,8 @@
             return Truffle.getRuntime().createCallTarget(new SLForeignCallerRootNode());
         } else if (Message.IS_NULL.equals(tree)) {
             return Truffle.getRuntime().createCallTarget(new SLForeignNullCheckNode());
+        } else if (Message.IS_EXECUTABLE.equals(tree)) {
+            return Truffle.getRuntime().createCallTarget(new SLForeignExecutableCheckNode());
         } else {
             throw new IllegalArgumentException(tree.toString() + " not supported");
         }
@@ -93,7 +95,12 @@
             // function call (the this object)
             // as an implicit 1st argument; we need to ignore this argument for SL
             List args = ForeignAccess.getArguments(frame);
-            Object[] arr = args.subList(1, args.size()).toArray();
+            Object[] arr;
+            if (args.size() > 0 && args.get(0) instanceof SLContext) {
+                arr = args.subList(1, args.size()).toArray();
+            } else {
+                arr = args.toArray();
+            }
             for (int i = 0; i < arr.length; i++) {
                 Object a = arr[i];
                 if (a instanceof Long) {
@@ -122,4 +129,16 @@
             return SLNull.SINGLETON == receiver;
         }
     }
+
+    private static class SLForeignExecutableCheckNode extends RootNode {
+        public SLForeignExecutableCheckNode() {
+            super(SLLanguage.class, null, null);
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object receiver = ForeignAccess.getReceiver(frame);
+            return receiver instanceof SLFunction;
+        }
+    }
 }
diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLObjectType.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLObjectType.java	Mon Sep 07 17:07:20 2015 +0200
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.truffle.sl.runtime;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.api.object.ObjectType;
+import com.oracle.truffle.api.object.Property;
+import com.oracle.truffle.sl.SLLanguage;
+
+final class SLObjectType extends ObjectType implements ForeignAccess.Factory10, ForeignAccess.Factory {
+    private final ForeignAccess access;
+
+    public SLObjectType() {
+        this.access = ForeignAccess.create(null, this);
+    }
+
+    @Override
+    public ForeignAccess getForeignAccessFactory() {
+        return access;
+    }
+
+    @Override
+    public CallTarget accessIsNull() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessIsExecutable() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessIsBoxed() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessHasSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessGetSize() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessUnbox() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessRead() {
+        return Truffle.getRuntime().createCallTarget(new SLForeignReadNode());
+    }
+
+    @Override
+    public CallTarget accessWrite() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessExecute(int argumentsLength) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessInvoke(int argumentsLength) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessNew(int argumentsLength) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public CallTarget accessMessage(Message unknown) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean canHandle(TruffleObject obj) {
+        return SLContext.isSLObject(obj);
+    }
+
+    private static class SLForeignReadNode extends RootNode {
+
+        public SLForeignReadNode() {
+            super(SLLanguage.class, null, null);
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            String fieldName = (String) ForeignAccess.getArguments(frame).get(0);
+            DynamicObject obj = (DynamicObject) ForeignAccess.getReceiver(frame);
+            Property property = obj.getShape().getProperty(fieldName);
+            return obj.get(property.getKey());
+        }
+
+    }
+}
diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/LongBinaryOperation.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/LongBinaryOperation.java	Mon Sep 07 17:07:20 2015 +0200
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.tck;
+
+/**
+ * Binary operation on numbers. Mimics "functional interface" - e.g. has just a single method, so it
+ * should be easily usable with lamdas.
+ */
+public interface LongBinaryOperation {
+    long compute(long a, long b);
+}
diff -r 025869c88840 -r e70b20f4bb00 truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java
--- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java	Fri Sep 04 16:41:38 2015 +0200
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java	Mon Sep 07 17:07:20 2015 +0200
@@ -24,21 +24,7 @@
  */
 package com.oracle.truffle.tck;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.dsl.NodeChild;
-import com.oracle.truffle.api.dsl.NodeChildren;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
-
-final class MaxMinObject implements TruffleObject {
+final class MaxMinObject implements LongBinaryOperation {
     private final boolean max;
 
     public MaxMinObject(boolean max) {
@@ -46,159 +32,7 @@
     }
 
     @Override
-    public ForeignAccess getForeignAccess() {
-        return ForeignAccess.create(MaxMinObject.class, new AF());
-    }
-
-    static final class AF implements ForeignAccess.Factory10 {
-        @Override
-        public CallTarget accessIsNull() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessIsExecutable() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessIsBoxed() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessHasSize() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessGetSize() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessUnbox() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessRead() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessWrite() {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessExecute(int argumentsLength) {
-            if (argumentsLength == 2) {
-                MaxMinNode maxNode = MaxMinObjectFactory.MaxMinNodeGen.create(new ReadReceiverNode(), MaxMinObjectFactory.UnboxNodeGen.create(new ReadArgNode(0)),
-                                MaxMinObjectFactory.UnboxNodeGen.create(new ReadArgNode(1)));
-                return Truffle.getRuntime().createCallTarget(maxNode);
-            }
-            return null;
-        }
-
-        @Override
-        public CallTarget accessInvoke(int argumentsLength) {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessMessage(Message unknown) {
-            return null;
-        }
-
-        @Override
-        public CallTarget accessNew(int argumentsLength) {
-            return null;
-        }
-    }
-
-    static class ReadArgNode extends Node {
-        private final int argIndex;
-
-        public ReadArgNode(int argIndex) {
-            this.argIndex = argIndex;
-        }
-
-        public Object execute(VirtualFrame frame) {
-            return ForeignAccess.getArguments(frame).get(argIndex);
-        }
-    }
-
-    static class ReadReceiverNode extends Node {
-        public Object execute(VirtualFrame frame) {
-            return ForeignAccess.getReceiver(frame);
-        }
-    }
-
-    @NodeChildren({@NodeChild(value = "valueNode", type = ReadArgNode.class)})
-    abstract static class UnboxNode extends Node {
-        @Child private Node unbox;
-        @Child private Node isBoxed;
-
-        public abstract Object executeUnbox(VirtualFrame frame);
-
-        @Specialization
-        public int executeUnbox(int value) {
-            return value;
-        }
-
-        @Specialization
-        public long executeUnbox(long value) {
-            return value;
-        }
-
-        @Specialization
-        public String executeUnbox(String value) {
-            return value;
-        }
-
-        @Specialization(guards = "isBoxedPrimitive(frame, foreignValue)")
-        public Object executeUnbox(VirtualFrame frame, TruffleObject foreignValue) {
-            if (unbox == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                unbox = insert(Message.UNBOX.createNode());
-            }
-            return ForeignAccess.execute(unbox, frame, foreignValue);
-        }
-
-        protected final boolean isBoxedPrimitive(VirtualFrame frame, TruffleObject object) {
-            if (isBoxed == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                isBoxed = insert(Message.IS_BOXED.createNode());
-            }
-            return (boolean) ForeignAccess.execute(isBoxed, frame, object);
-        }
-
-    }
-
-    @NodeChildren({@NodeChild(value = "receiver", type = ReadReceiverNode.class), @NodeChild(value = "firstNode", type = UnboxNode.class), @NodeChild(value = "secondNode", type = UnboxNode.class)})
-    abstract static class MaxMinNode extends RootNode {
-
-        MaxMinNode() {
-            super(MMLanguage.class, null, null);
-        }
-
-        @Specialization
-        public int execute(MaxMinObject receiver, int first, int second) {
-            return receiver.max ? Math.max(first, second) : Math.min(first, second);
-        }
-
-        @Specialization
-        public long execute(MaxMinObject receiver, long first, long second) {
-            return receiver.max ? Math.max(first, second) : Math.min(first, second);
-        }
-
-        @Specialization
-        public double execute(MaxMinObject receiver, double first, double second) {
-            return receiver.max ? Math.max(first, second) : Math.min(first, second);
-        }
-    }
-
-    private abstract class MMLanguage extends TruffleLanguage {
+    public long compute(long a, long b) {
+        return max ? Math.max(a, b) : Math.min(a, b);
     }
 }
diff -r 025869c88840 -r e70b20f4bb00 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	Fri Sep 04 16:41:38 2015 +0200
+++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/TruffleTCK.java	Mon Sep 07 17:07:20 2015 +0200
@@ -24,10 +24,14 @@
  */
 package com.oracle.truffle.tck;
 
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.TruffleVM;
 import java.io.IOException;
 import java.util.Random;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import static org.junit.Assert.*;
 import org.junit.Test;
 
@@ -37,6 +41,8 @@
  * include in your test suite.
  */
 public abstract class TruffleTCK {
+    private static final Logger LOG = Logger.getLogger(TruffleTCK.class.getName());
+    private static final Random RANDOM = new Random();
     private TruffleVM tckVM;
 
     protected TruffleTCK() {
@@ -56,7 +62,7 @@
     protected abstract TruffleVM prepareVM() throws Exception;
 
     /**
-     * Mimetype associated with your language. The mimetype will be passed to
+     * MIME type associated with your language. The MIME type will be passed to
      * {@link TruffleVM#eval(com.oracle.truffle.api.source.Source)} method of the
      * {@link #prepareVM() created TruffleVM}.
      *
@@ -124,6 +130,31 @@
      */
     protected abstract String invalidCode();
 
+    /**
+     * Name of a function that returns a compound object with members representing certain
+     * operations. In the JavaScript the object should look like:
+     * 
+     * 
+     * var obj = {
+     *   'fourtyTwo': function {@link #fourtyTwo()},
+     *   'plus': function {@link #plusInt()},
+     *   'returnsNull': function {@link #returnsNull()},
+     *   'returnsThis': function() { return obj; }
+     * };
+     * return obj;
+     * 
+ * + * The returned object shall have three functions that will be obtained and used exactly as + * described in their Javadoc - e.g. {@link #fourtyTwo()}, {@link #plusInt()} and + * {@link #returnsNull()}. In addition to that there should be one more function + * returnsThis that will return the object itself again. + * + * @return name of a function that returns such compound object + */ + protected String compoundObject() { + return null; + } + private TruffleVM vm() throws Exception { if (tckVM == null) { tckVM = prepareVM(); @@ -149,10 +180,17 @@ } @Test - public void testNull() throws Exception { - if (getClass() == TruffleTCK.class) { + public void testFortyTwoWithCompoundObject() throws Exception { + CompoundObject obj = findCompoundSymbol("testFortyTwoWithCompoundObject"); + if (obj == null) { return; } + Number res = obj.fourtyTwo(); + assertEquals("Should be 42", 42, res.intValue()); + } + + @Test + public void testNull() throws Exception { TruffleVM.Symbol retNull = findGlobalSymbol(returnsNull()); Object res = retNull.invoke(null).get(); @@ -161,19 +199,37 @@ } @Test + public void testNullInCompoundObject() throws Exception { + CompoundObject obj = findCompoundSymbol("testNullInCompoundObject"); + if (obj == null) { + return; + } + Object res = obj.returnsNull(); + assertNull("Should yield real Java null", res); + } + + @Test public void testPlusWithInts() throws Exception { - Random r = new Random(); - int a = r.nextInt(100); - int b = r.nextInt(100); + int a = RANDOM.nextInt(100); + int b = RANDOM.nextInt(100); TruffleVM.Symbol plus = findGlobalSymbol(plusInt()); - Object res = plus.invoke(null, a, b).get(); + Number n = plus.invoke(null, a, b).as(Number.class); + assert a + b == n.intValue() : "The value is correct: (" + a + " + " + b + ") = " + n.intValue(); + } - assert res instanceof Number : "+ on two ints should yield a number, but was: " + res; + @Test + public void testPlusWithIntsOnCompoundObject() throws Exception { + int a = RANDOM.nextInt(100); + int b = RANDOM.nextInt(100); - Number n = (Number) res; + CompoundObject obj = findCompoundSymbol("testPlusWithIntsOnCompoundObject"); + if (obj == null) { + return; + } + Number n = obj.plus(a, b); assert a + b == n.intValue() : "The value is correct: (" + a + " + " + b + ") = " + n.intValue(); } @@ -189,7 +245,8 @@ public void testMaxOrMinValue() throws Exception { TruffleVM.Symbol apply = findGlobalSymbol(applyNumbers()); - Object res = apply.invoke(null, new MaxMinObject(true)).get(); + TruffleObject fn = JavaInterop.asTruffleFunction(LongBinaryOperation.class, new MaxMinObject(true)); + Object res = apply.invoke(null, fn).get(); assert res instanceof Number : "result should be a number: " + res; @@ -202,12 +259,17 @@ public void testMaxOrMinValue2() throws Exception { TruffleVM.Symbol apply = findGlobalSymbol(applyNumbers()); - Object res = apply.invoke(null, new MaxMinObject(false)).get(); + TruffleObject fn = JavaInterop.asTruffleFunction(LongBinaryOperation.class, new MaxMinObject(false)); + final TruffleVM.Symbol result = apply.invoke(null, fn); - assert res instanceof Number : "result should be a number: " + res; + try { + String res = result.as(String.class); + fail("Cannot be converted to String: " + res); + } catch (ClassCastException ex) { + // correct + } - Number n = (Number) res; - + Number n = result.as(Number.class); assert 28 == n.intValue() : "18 < 32 and plus 10"; } @@ -224,18 +286,19 @@ int prev1 = 0; int prev2 = 0; - Random r = new Random(); for (int i = 0; i < 10; i++) { - int quantum = r.nextInt(10); + int quantum = RANDOM.nextInt(10); for (int j = 0; j < quantum; j++) { Object res = count1.invoke(null).get(); assert res instanceof Number : "expecting number: " + res; - assert ((Number) res).intValue() == ++prev1 : "expecting " + prev1 + " but was " + res; + ++prev1; + assert ((Number) res).intValue() == prev1 : "expecting " + prev1 + " but was " + res; } for (int j = 0; j < quantum; j++) { Object res = count2.invoke(null).get(); assert res instanceof Number : "expecting number: " + res; - assert ((Number) res).intValue() == ++prev2 : "expecting " + prev2 + " but was " + res; + ++prev2; + assert ((Number) res).intValue() == prev2 : "expecting " + prev2 + " but was " + res; } assert prev1 == prev2 : "At round " + i + " the same number of invocations " + prev1 + " vs. " + prev2; } @@ -247,4 +310,36 @@ assert s != null : "Symbol " + name + " is not found!"; return s; } + + private CompoundObject findCompoundSymbol(String name) throws Exception { + final String compoundObjectName = compoundObject(); + if (compoundObjectName == null) { + final long introduced = 1441616302340L; + long wait = (System.currentTimeMillis() - introduced) / 3600; + if (wait < 100) { + wait = 100; + } + LOG.log(Level.SEVERE, "compoundObject() method not overriden! Skipping {1} test for now. But sleeping for {0} ms.", new Object[]{wait, name}); + Thread.sleep(wait); + return null; + } + TruffleVM.Symbol s = vm().findGlobalSymbol(compoundObjectName); + assert s != null : "Symbol " + compoundObjectName + " is not found!"; + CompoundObject obj = s.invoke(null).as(CompoundObject.class); + int traverse = RANDOM.nextInt(10); + while (traverse-- >= 0) { + obj = obj.returnsThis(); + } + return obj; + } + + interface CompoundObject { + Number fourtyTwo(); + + Number plus(int x, int y); + + Object returnsNull(); + + CompoundObject returnsThis(); + } }