/*
* Copyright (c) 2012, 2013, 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.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
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.PolyglotEngine;
import com.oracle.truffle.sl.SLLanguage;
import com.oracle.truffle.sl.runtime.SLFunction;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
public class SLJavaInteropTest {
private PolyglotEngine engine;
private ByteArrayOutputStream os;
@Before
public void create() {
os = new ByteArrayOutputStream();
engine = PolyglotEngine.newBuilder().setOut(os).build();
}
@After
public void dispose() {
engine.dispose();
}
@Test
public void asFunction() throws Exception {
String scriptText = "function test() {\n" + " println(\"Called!\");\n" + "}\n";
Source script = Source.newBuilder(scriptText).name("Test").mimeType(SLLanguage.MIME_TYPE).build();
engine.eval(script);
PolyglotEngine.Value main = engine.findGlobalSymbol("test");
final Object value = main.get();
assertTrue("It's truffle object", value instanceof TruffleObject);
SLFunction rawFunction = main.as(SLFunction.class);
assertNotNull("One can get the type of the inner Truffle Object", rawFunction);
Runnable runnable = JavaInterop.asJavaFunction(Runnable.class, (TruffleObject) value);
runnable.run();
assertEquals("Called!\n", os.toString("UTF-8"));
}
@Test
public void asFunctionWithArg() throws Exception {
String scriptText = "function values(a, b) {\n" + //
" println(\"Called with \" + a + \" and \" + b);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
final Object value = fn.get();
assertTrue("It's truffle object", value instanceof TruffleObject);
PassInValues valuesIn = JavaInterop.asJavaFunction(PassInValues.class, (TruffleObject) value);
valuesIn.call("OK", "Fine");
assertEquals("Called with OK and Fine\n", os.toString("UTF-8"));
}
private static void assertNumber(double exp, Object real) {
if (real instanceof Number) {
assertEquals(exp, ((Number) real).doubleValue(), 0.1);
} else {
fail("Expecting a number, but was " + real);
}
}
interface PassInValues {
void call(Object a, Object b);
}
@Test
public void asFunctionWithArr() throws Exception {
String scriptText = "function values(a, b) {\n" + //
" println(\"Called with \" + a[0] + a[1] + \" and \" + b);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
final Object value = fn.get();
assertTrue("It's truffle object", value instanceof TruffleObject);
PassInArray valuesIn = JavaInterop.asJavaFunction(PassInArray.class, (TruffleObject) value);
valuesIn.call(new Object[]{"OK", "Fine"});
assertEquals("Called with OKFine and null\n", os.toString("UTF-8"));
}
@Test
public void asFunctionWithVarArgs() throws Exception {
String scriptText = "function values(a, b) {\n" + //
" println(\"Called with \" + a + \" and \" + b);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
final Object value = fn.get();
assertTrue("It's truffle object", value instanceof TruffleObject);
PassInVarArg valuesIn = JavaInterop.asJavaFunction(PassInVarArg.class, (TruffleObject) value);
valuesIn.call("OK", "Fine");
assertEquals("Called with OK and Fine\n", os.toString("UTF-8"));
}
@Test
public void asFunctionWithArgVarArgs() throws Exception {
String scriptText = "function values(a, b, c) {\n" + //
" println(\"Called with \" + a + \" and \" + b + c);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
final Object value = fn.get();
assertTrue("It's truffle object", value instanceof TruffleObject);
PassInArgAndVarArg valuesIn = JavaInterop.asJavaFunction(PassInArgAndVarArg.class, (TruffleObject) value);
valuesIn.call("OK", "Fine", "Well");
assertEquals("Called with OK and FineWell\n", os.toString("UTF-8"));
}
@Test
public void sumPairs() {
String scriptText = "function values(sum, k, v) {\n" + //
" obj = new();\n" + //
" obj.key = k;\n" + //
" obj.value = v;\n" + //
" return sum.sum(obj);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
Sum javaSum = new Sum();
Object sum = javaSum;
Object ret1 = fn.execute(sum, "one", 1).get();
Object ret2 = fn.execute(sum, "two", 2).as(Object.class);
Sum ret3 = fn.execute(sum, "three", 3).as(Sum.class);
assertEquals(6, javaSum.sum);
assertSame(ret1, ret2);
assertSame(ret3, ret2);
assertSame(sum, ret2);
}
@Test
public void sumPairsFunctionalInterface() {
String scriptText = "function values(sum, k, v) {\n" + //
" obj = new();\n" + //
" obj.key = k;\n" + //
" obj.value = v;\n" + //
" return sum.sum(obj);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
Values fn = engine.findGlobalSymbol("values").as(Values.class);
Sum sum = new Sum();
Object ret1 = fn.values(sum, "one", 1);
Object ret2 = fn.values(sum, "two", 2);
Object ret3 = fn.values(sum, "three", 3);
assertEquals(6, sum.sum);
assertSame(ret1, ret2);
assertSame(ret3, ret2);
assertSame(sum, ret2);
}
@Test
public void sumPairsFunctionalRawInterface() {
String scriptText = "function values(sum, k, v) {\n" + //
" obj = new();\n" + //
" obj.key = k;\n" + //
" obj.value = v;\n" + //
" return sum.sum(obj);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
ValuesRaw fn = engine.findGlobalSymbol("values").as(ValuesRaw.class);
Sum sum = new Sum();
Object ret1 = fn.values(sum, "one", 1);
Object ret2 = fn.values(sum, "two", 2);
Object ret3 = fn.values(sum, "three", 3);
assertEquals(6, sum.sum);
assertSame(ret1, ret2);
assertSame(ret3, ret2);
assertSame(sum, ret2);
}
@Test
public void sumPairsIndirect() {
String scriptText = "function values(sum, k, v) {\n" + //
" obj = new();\n" + //
" obj.key = k;\n" + //
" obj.value = v;\n" + //
" return sum.sum(obj);\n" + //
"}\n" + //
"function create() {\n" + //
" obj = new();\n" + //
" obj.doSum1 = values;\n" + //
" obj.doSum2 = values;\n" + //
" return obj;\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
DoSums fn = engine.findGlobalSymbol("create").execute().as(DoSums.class);
Sum sum = new Sum();
Object ret1 = fn.doSum1(sum, "one", 1);
Sum ret2 = fn.doSum2(sum, "two", 2);
Object ret3 = fn.doSum1(sum, "three", 3);
assertEquals(6, sum.sum);
assertSame(ret1, ret2);
assertSame(ret3, ret2);
assertSame(sum, ret2);
}
@Test
public void sumPairsInArray() {
String scriptText = "function values(sum, arr) {\n" + //
" sum.sumArray(arr);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
Sum javaSum = new Sum();
PairImpl[] arr = {
new PairImpl("one", 1),
new PairImpl("two", 2),
new PairImpl("three", 3),
};
fn.execute(javaSum, arr);
assertEquals(6, javaSum.sum);
}
@Test
public void sumPairsInArrayOfArray() {
String scriptText = "function values(sum, arr) {\n" + //
" sum.sumArrayArray(arr);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
Sum javaSum = new Sum();
PairImpl[][] arr = {
new PairImpl[]{
new PairImpl("one", 1),
},
new PairImpl[]{
new PairImpl("two", 2),
new PairImpl("three", 3),
}
};
fn.execute(javaSum, arr);
assertEquals(6, javaSum.sum);
}
@Test
public void sumMapInArrayOfArray() {
String scriptText = "function values(sum, arr) {\n" + //
" sum.sumArrayMap(arr);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
Sum javaSum = new Sum();
PairImpl[][] arr = {
new PairImpl[]{
new PairImpl("one", 1),
},
new PairImpl[]{
new PairImpl("two", 2),
new PairImpl("three", 3),
}
};
fn.execute(javaSum, arr);
assertEquals(6, javaSum.sum);
}
@Test
public void sumPairInMapOfArray() {
String scriptText = "function values(sum, arr) {\n" + //
" sum.sumMapArray(arr);\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value fn = engine.findGlobalSymbol("values");
Sum javaSum = new Sum();
TwoPairsImpl groups = new TwoPairsImpl(
new PairImpl[]{
new PairImpl("one", 1),
},
new PairImpl[]{
new PairImpl("two", 2),
new PairImpl("three", 3),
});
fn.execute(javaSum, groups);
assertEquals(6, javaSum.sum);
}
@Test
public void accessJavaMap() {
String scriptText = "function write(map, key, value) {\n" + //
" map[key] = value;\n" + //
"}\n" + //
"function read(map, key) {\n" + //
" return map[key];\n" + //
"}\n"; //
Source script = Source.newBuilder(scriptText).name("Test").mimeType("application/x-sl").build();
engine.eval(script);
PolyglotEngine.Value read = engine.findGlobalSymbol("read");
PolyglotEngine.Value write = engine.findGlobalSymbol("write");
Map<Object, Object> map = new HashMap<>();
map.put("a", 42);
Object b = read.execute(map, "a").get();
assertNumber(42L, b);
write.execute(map, "a", 33);
Object c = read.execute(map, "a").get();
assertNumber(33L, c);
}
@FunctionalInterface
interface Values {
Sum values(Sum sum, String key, int value);
}
@FunctionalInterface
interface ValuesRaw {
Object values(Object sum, String key, int value);
}
interface DoSums {
Object doSum1(Sum sum, String key, int value);
Sum doSum2(Sum sum, String key, Integer value);
}
interface PassInArray {
void call(Object[] arr);
}
interface PassInVarArg {
void call(Object... arr);
}
interface PassInArgAndVarArg {
void call(Object first, Object... arr);
}
public interface Pair {
String key();
int value();
}
public static final class PairImpl {
public final String key;
public final int value;
PairImpl(String key, int value) {
this.key = key;
this.value = value;
}
}
public static final class TwoPairsImpl {
public final PairImpl[] one;
public final PairImpl[] two;
TwoPairsImpl(PairImpl[] one, PairImpl[] two) {
this.one = one;
this.two = two;
}
}
public static class Sum {
int sum;
public Sum sum(Pair p) {
sum += p.value();
return this;
}
public void sumArray(List<Pair> pairs) {
Object[] arr = pairs.toArray();
assertNotNull("Array created", arr);
for (Pair p : pairs) {
sum(p);
}
}
public void sumArrayArray(List<List<Pair>> pairs) {
Object[] arr = pairs.toArray();
assertNotNull("Array created", arr);
assertEquals("Two lists", 2, arr.length);
for (List<Pair> list : pairs) {
sumArray(list);
}
}
public void sumArrayMap(List<List<Map<String, Integer>>> pairs) {
Object[] arr = pairs.toArray();
assertNotNull("Array created", arr);
assertEquals("Two lists", 2, arr.length);
for (List<Map<String, Integer>> list : pairs) {
for (Map<String, Integer> map : list) {
Integer value = map.get("value");
sum += value;
}
}
}
public void sumMapArray(Map<String, List<Pair>> pairs) {
assertEquals("Two elements", 2, pairs.size());
Object one = pairs.get("one");
assertNotNull(one);
Object two = pairs.get("two");
assertNotNull(two);
sumArray(pairs.get("two"));
sumArray(pairs.get("one"));
}
}
}