/*
* Copyright (c) 2017, 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.nfi.test;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.InteropException;
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.nodes.Node;
import com.oracle.truffle.nfi.test.interop.BoxedPrimitive;
import com.oracle.truffle.nfi.test.interop.TestCallback;
import com.oracle.truffle.tck.TruffleRunner;
import com.oracle.truffle.tck.TruffleRunner.Inject;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(TruffleRunner.class)
public class StringNFITest extends NFITest {
public static class StringArgNode extends SendExecuteNode {
public StringArgNode() {
super("string_arg", "(string):sint32", 1);
}
}
@Test
public void testJavaStringArg(@Inject(StringArgNode.class) CallTarget callTarget) {
Object ret = callTarget.call("42");
Assert.assertThat("return value", ret, is(instanceOf(Integer.class)));
Assert.assertEquals("return value", 42, (int) (Integer) ret);
}
@Test
public void testBoxedStringArg(@Inject(StringArgNode.class) CallTarget callTarget) {
Object ret = callTarget.call(new BoxedPrimitive("42"));
Assert.assertThat("return value", ret, is(instanceOf(Integer.class)));
Assert.assertEquals("return value", 42, (int) (Integer) ret);
}
public static class NativeStringArgNode extends NFITestRootNode {
final TruffleObject function = lookupAndBind("string_arg", "(string):sint32");
final TruffleObject strdup = lookupAndBind(defaultLibrary, "strdup", "(string):string");
final TruffleObject free = lookupAndBind(defaultLibrary, "free", "(pointer):void");
@Child Node executeFunction = Message.createExecute(1).createNode();
@Child Node executeStrdup = Message.createExecute(1).createNode();
@Child Node executeFree = Message.createExecute(1).createNode();
@Override
public Object executeTest(VirtualFrame frame) throws InteropException {
Object nativeString = ForeignAccess.sendExecute(executeStrdup, strdup, frame.getArguments()[0]);
Object ret = ForeignAccess.sendExecute(executeFunction, function, nativeString);
ForeignAccess.sendExecute(executeFree, free, nativeString);
return ret;
}
}
@Test
public void testNativeStringArg(@Inject(NativeStringArgNode.class) CallTarget callTarget) {
Object retObj = callTarget.call("8472");
Assert.assertThat("return value", retObj, is(instanceOf(Integer.class)));
Assert.assertEquals("return value", 8472, (int) (Integer) retObj);
}
public static class StringRetConstNode extends SendExecuteNode {
public StringRetConstNode() {
super("string_ret_const", "():string", 0);
}
}
@Test
public void testStringRetConst(@Inject(StringRetConstNode.class) CallTarget callTarget) {
Object ret = callTarget.call();
Assert.assertThat("return value", ret, is(instanceOf(TruffleObject.class)));
TruffleObject obj = (TruffleObject) ret;
Assert.assertTrue("isBoxed", JavaInterop.isBoxed(obj));
Assert.assertEquals("return value", "Hello, World!", JavaInterop.unbox(obj));
}
public static class StringRetDynamicNode extends NFITestRootNode {
final TruffleObject function = lookupAndBind("string_ret_dynamic", "(sint32):string");
final TruffleObject free = lookupAndBind("free_dynamic_string", "(pointer):sint32");
@Child Node executeFunction = Message.createExecute(1).createNode();
@Child Node executeFree = Message.createExecute(1).createNode();
@Override
public Object executeTest(VirtualFrame frame) throws InteropException {
Object ret = ForeignAccess.sendExecute(executeFunction, function, frame.getArguments()[0]);
checkRet(ret);
/*
* Normally here we'd just call "free" from libc. We're using a wrapper to be able to
* reliably test whether it was called with the correct argument.
*/
Object magic = ForeignAccess.sendExecute(executeFree, free, ret);
assertEquals(42, magic);
return null;
}
@TruffleBoundary
private static void checkRet(Object ret) {
Assert.assertThat("return value", ret, is(instanceOf(TruffleObject.class)));
TruffleObject obj = (TruffleObject) ret;
Assert.assertTrue("isBoxed", JavaInterop.isBoxed(obj));
Assert.assertEquals("return value", "42", JavaInterop.unbox(obj));
}
}
@Test
public void testStringRetDynamic(@Inject(StringRetDynamicNode.class) CallTarget target) {
target.call(42);
}
public static class StringCallbackNode extends SendExecuteNode {
public StringCallbackNode() {
super("string_callback", "( (string):sint32, ():string ) : sint32", 2);
}
}
private static void testStringCallback(CallTarget target, Object callbackRet) {
TruffleObject strArgCallback = new TestCallback(1, (args) -> {
Assert.assertEquals("string argument", "Hello, Truffle!", args[0]);
return 42;
});
TruffleObject strRetCallback = new TestCallback(0, (args) -> {
return callbackRet;
});
Object ret = target.call(strArgCallback, strRetCallback);
Assert.assertThat("return value", ret, is(instanceOf(Integer.class)));
Assert.assertEquals("return value", 42, (int) (Integer) ret);
}
@Test
public void testStringCallback(@Inject(StringCallbackNode.class) CallTarget target) {
testStringCallback(target, "Hello, Native!");
}
@Test
public void testBoxedStringCallback(@Inject(StringCallbackNode.class) CallTarget target) {
testStringCallback(target, new BoxedPrimitive("Hello, Native!"));
}
public static class NativeStringCallbackNode extends NFITestRootNode {
final TruffleObject stringRetConst = lookupAndBind("string_ret_const", "():string");
final TruffleObject nativeStringCallback = lookupAndBind("native_string_callback", "(():string) : string");
@Child Node executeStringRetConst = Message.createExecute(0).createNode();
@Child Node executeNativeStringCallback = Message.createExecute(1).createNode();
@Override
public Object executeTest(VirtualFrame frame) throws InteropException {
Object string = ForeignAccess.sendExecute(executeStringRetConst, stringRetConst);
TruffleObject callback = createCallback(string);
return ForeignAccess.sendExecute(executeNativeStringCallback, nativeStringCallback, callback);
}
@TruffleBoundary
private static TruffleObject createCallback(Object obj) {
return new TestCallback(0, (args) -> {
return obj;
});
}
}
@Test
public void testNativeStringCallback(@Inject(NativeStringCallbackNode.class) CallTarget target) {
Object ret = target.call();
Assert.assertThat("return value", ret, is(instanceOf(TruffleObject.class)));
TruffleObject obj = (TruffleObject) ret;
Assert.assertTrue("isBoxed", JavaInterop.isBoxed(obj));
Assert.assertEquals("return value", "same", JavaInterop.unbox(obj));
}
}