Mercurial > hg > truffle
changeset 22135:e70b20f4bb00
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.
line wrap: on
line diff
--- 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",
--- /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(); + } +}
--- /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); +}
--- /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; + } +}
--- /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<String> 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<String> 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<? extends TruffleLanguage> 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 + +}
--- /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()); + } +}
--- /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<String> stringArr(); + + List<Byte> byteArr(); + + List<Short> shortArr(); + + List<Integer> intArr(); + + List<Long> longArr(); + + List<Float> floatArr(); + + List<Double> doubleArr(); + + List<Character> charArr(); + + List<Boolean> 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<String> 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<? extends Number> numbers) { + double v = 0.0; + for (Number n : numbers) { + v += n.doubleValue(); + } + assertEquals(msg, expected, v, 0.05); + } +}
--- /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<Object> 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<Object> 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<? extends Object> 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); + } +}
--- /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); + } + +}
--- /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; + } + } + +}
--- /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; + } +}
--- /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<Object> 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); + } + } + +}
--- /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 <b>Java</b>/<em>Truffle</em> interop builds on + * {@link ForeignAccess mutual interoperability} between individual <em>Truffle</em> languages - it + * just encapsulates it into <b>Java</b> facade to make it as natural to access foreign + * {@link TruffleObject Truffle objects} as <b>Java</b> programmers are used to when accessing + * <b>Java</b> 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 <em>JavaScript</em> object like: + * + * <pre> + * var obj = { + * 'x' : 10, + * 'y' : 3.3, + * 'name' : 'Truffle' + * }; + * </pre> + * + * from <b>Java</b>. One can do it by defining an interface: + * + * <pre> + * <b>interface</b> ObjAccess { + * int x(); + * {@link MethodMessage @MethodMessage}(message = "WRITE") + * void x(int newValue); + * double y(); + * String name(); + * } + * </pre> + * + * and obtaining its instance by calling this conversion method: + * + * <pre> + * ObjAccess access = JavaInterop.{@link #asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) asJavaObject}(ObjAccess.<b>class</b>, obj); + * <b>assert</b> access.x() == 10 : "Still the default"; + * access.x(5); + * <b>assert</b> access.x() == 5 : "Changed to five"; + * </pre> + * + * @param <T> type of requested and returned value + * @param type interface modeling structure of <code>foreignObject</code> in <b>Java</b> + * @param foreignObject object coming from a {@link TruffleObject Truffle language} + * @return instance of requested interface granting access to specified + * <code>foreignObject</code> + */ + public static <T> T asJavaObject(Class<T> 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 <b>public</b> fields and methods to any <em>Truffle</em> + * language. An instance of class + * + * <pre> + * <b>class</b> JavaRecord { + * <b>public int</b> x; + * <b>public double</b> y; + * <b>public</b> String name() { + * <b>return</b> "Truffle"; + * } + * } + * {@link TruffleObject} obj = JavaInterop.asTruffleObject(new JavaRecord()); + * </pre> + * + * can then be access from <em>JavaScript</em> or any other <em>Truffle</em> based language as + * + * <pre> + * obj.x; + * obj.y; + * obj.name(); + * </pre> + * + * When the <code>obj</code> represents a {@link Class}, then the created {@link TruffleObject} + * will allow access to <b>public</b> and <b>static</b> fields and methods from the class. + * + * @param obj a Java object to convert into one suitable for <em>Truffle</em> 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 + * <b>Java</b> <em>functional interface</em>. + * + * @param <T> requested and returned type + * @param functionalType interface with a single defined method - so called + * <em>functional interface</em> + * @param function <em>Truffle</em> that responds to {@link Message#IS_EXECUTABLE} and can be + * invoked + * @return instance of interface that wraps the provided <code>function</code> + */ + public static <T> T asJavaFunction(Class<T> 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 <em>Truffle</em> languages. Here is a definition of + * function returning the meaning of life as lambda expression, converting it back to + * <b>Java</b> and using it: + * + * <pre> + * TruffleObject to = JavaInterop.asTruffleFunction(Callable.<b>class</b>, () -> 42); + * Callable c = JavaInterop.{@link #asJavaFunction(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) asJavaFunction}(Callable.<b>class</b>, to); + * <b>assert</b> c.call() == 42; + * </pre> + * + * @param <T> requested interface and implementation + * @param functionalType interface with a single defined method - so called + * <em>functional interface</em> + * @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 <em>Truffle</em> language + */ + public static <T> TruffleObject asTruffleFunction(Class<T> 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<Object> 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<Object> 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<Object> 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<? extends TruffleLanguage> 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 + +}
--- /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<Object> { +}
--- /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<Object> 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; + } + +}
--- /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; + } +}
--- /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<Object> args = ForeignAccess.getArguments(frame); + return JavaFunctionNode.execute(f, args.subList(1, args.size()).toArray()); + } + +}
--- /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. <h5> + * Writing to a field</h5> For example to write to field x of a JSON object: + * + * <pre> + * var obj = { 'x' : 5 } + * </pre> + * + * one can define the appropriate wrapper interface as: + * + * <pre> + * <b>interface</b> ObjInterop { + * {@link MethodMessage @MethodMessage}(message = <em>"WRITE"</em>) + * <b>void</b> x(int value); + * } + * </pre> + * + * Then one can change the value of field <em>x</em> in <em>obj</em> from Java by calling: + * + * <pre> + * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject) JavaInterop.asJavaObject(ObjInterop.<b>class</b>, obj).x(10); + * </pre> + * + * the value of the <em>x</em> field is going to be <em>10</em> 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. <em>"READ"</em>, <em>"WRITE"</em>, <em>"UNBOX"</em>, + * <em>IS_NULL</em>) or slightly mangled names of {@link Message} class factory methods ( + * <em>EXECUTE</em>, <em>INVOKE</em>). 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(); +}
--- /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; + } + +}
--- /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); + } + +}
--- /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); + } + } + +}
--- /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); + } + +}
--- /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<T> extends AbstractList<T> { + private final TruffleObject array; + private final Class<T> type; + + private TruffleList(Class<T> elementType, TruffleObject array) { + this.array = array; + this.type = elementType; + } + + public static <T> List<T> create(Class<T> 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); + } + +}
--- /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); + } + +}
--- /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); + } + } + +}
--- /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 <b>Java</b> and + * {@link com.oracle.truffle.api.interop.TruffleObject Truffle objects}. + */ +package com.oracle.truffle.api.interop.java; \ No newline at end of file
--- 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"; } } }
--- 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 <code>null</code> if this message is not * supported */ @@ -197,7 +197,7 @@ /** * Handles {@link Message#IS_BOXED} message. - * + * * @return call target to handle the message or <code>null</code> if this message is not * supported */ @@ -205,7 +205,7 @@ /** * Handles {@link Message#HAS_SIZE} message. - * + * * @return call target to handle the message or <code>null</code> if this message is not * supported */ @@ -213,7 +213,7 @@ /** * Handles {@link Message#GET_SIZE} message. - * + * * @return call target to handle the message or <code>null</code> if this message is not * supported */ @@ -221,7 +221,7 @@ /** * Handles {@link Message#UNBOX} message. - * + * * @return call target to handle the message or <code>null</code> if this message is not * supported */ @@ -229,7 +229,7 @@ /** * Handles {@link Message#READ} message. - * + * * @return call target to handle the message or <code>null</code> if this message is not * supported */ @@ -237,7 +237,7 @@ /** * Handles {@link Message#WRITE} message. - * + * * @return call target to handle the message or <code>null</code> 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 <code>null</code> 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 <code>null</code> 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 <code>null</code> if this message is not * supported
--- 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; } }
--- 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; }
--- 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"; - } }
--- 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"; - } }
--- 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"; - } }
--- 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"; - } }
--- 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); + } }
--- 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); + } + } + } + } }
--- 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"; - } }
--- 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(); }
--- 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"; - } }
--- 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"; - } - }
--- 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 <T> List<T> asList(T[] arr, int first, int last) {
--- /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; + } + } +}
--- 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<? extends TruffleLanguage<?>> 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<? extends TruffleLanguage> 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<? extends TruffleLanguage<?>> lang, Node foreignAccess, TruffleObject function, int argumentLength) { + @SuppressWarnings("rawtypes") + public TemporaryRoot(Class<? extends TruffleLanguage> lang, Node foreignAccess, TruffleObject function, int argumentLength) { super(lang, null, null); this.foreignAccess = foreignAccess; this.convert = new ConvertNode();
--- 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 <T> 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> T as(Class<T> 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
--- 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 <code>RootNode<code>: - * + * * <pre> * <code> * 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; + } + } + }
--- 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
--- 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()); } /**
--- 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<Object> 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; + } + } }
--- /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()); + } + + } +}
--- /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); +}
--- 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<Object> { + public long compute(long a, long b) { + return max ? Math.max(a, b) : Math.min(a, b); } }
--- 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: + * + * <pre> + * <b>var</b> obj = { + * 'fourtyTwo': function {@link #fourtyTwo()}, + * 'plus': function {@link #plusInt()}, + * 'returnsNull': function {@link #returnsNull()}, + * 'returnsThis': function() { return obj; } + * }; + * <b>return</b> obj; + * </pre> + * + * 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 + * <b>returnsThis</b> 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(); + } }