/* * 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.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.nodes.Node; 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.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(TruffleRunner.class) public class ObjectNFITest extends NFITest { private static TruffleObject nativeEnv; private static class TestObject implements TruffleObject { int intField; TestObject() { this(0); } TestObject(int value) { intField = value; } int readField(String field) { Assert.assertEquals("field name", "intField", field); return intField; } void writeField(String field, int value) { Assert.assertEquals("field name", "intField", field); intField = value; } @Override public ForeignAccess getForeignAccess() { Assert.fail("unexpected interop access to TestObject"); return null; } } interface ReadIntField { int read(TestObject obj, String field); } interface WriteIntField { void write(TestObject obj, String field, int value); } @BeforeClass public static void initEnv() { TruffleObject createNewObject = new TestCallback(0, (args) -> new TestObject()); TruffleObject readIntField = new TestCallback(2, (args) -> { Assert.assertThat("args[0]", args[0], is(instanceOf(TestObject.class))); Assert.assertThat("args[1]", args[1], is(instanceOf(String.class))); return ((TestObject) args[0]).readField((String) args[1]); }); TruffleObject writeIntField = new TestCallback(3, (args) -> { Assert.assertThat("args[0]", args[0], is(instanceOf(TestObject.class))); Assert.assertThat("args[1]", args[1], is(instanceOf(String.class))); Assert.assertThat("args[2]", args[2], is(instanceOf(Integer.class))); ((TestObject) args[0]).writeField((String) args[1], (Integer) args[2]); return null; }); TruffleObject initializeEnv = lookupAndBind("initialize_env", "( ():object, (object,string):sint32, (object,string,sint32):void ) : pointer"); try { nativeEnv = (TruffleObject) ForeignAccess.sendExecute(Message.createExecute(3).createNode(), initializeEnv, createNewObject, readIntField, writeIntField); } catch (InteropException ex) { throw new AssertionError(ex); } } @AfterClass public static void deleteEnv() { TruffleObject deleteEnv = lookupAndBind("delete_env", "(pointer):void"); try { ForeignAccess.sendExecute(Message.createExecute(1).createNode(), deleteEnv, nativeEnv); nativeEnv = null; } catch (InteropException ex) { throw new AssertionError(ex); } } public static class CopyAndIncrementNode extends SendExecuteNode { public CopyAndIncrementNode() { super("copy_and_increment", "(pointer, object) : object", 2); } } @Test public void testCopyAndIncrement(@Inject(CopyAndIncrementNode.class) CallTarget callTarget) { TestObject testObj = new TestObject(42); Object ret = callTarget.call(nativeEnv, testObj); Assert.assertThat("return value", ret, is(instanceOf(TestObject.class))); TestObject testRet = (TestObject) ret; Assert.assertNotSame("return value", testObj, testRet); Assert.assertEquals("intField", 43, testRet.intField); } private TestObject testArg; public class TestKeepObjectNode extends NFITestRootNode { final TruffleObject keepExistingObject = lookupAndBind("keep_existing_object", "(object):pointer"); final TruffleObject freeAndGetObject = lookupAndBind("free_and_get_object", "(pointer):object"); final TruffleObject freeAndGetContent = lookupAndBind("free_and_get_content", "(pointer, pointer):sint32"); @Child Node executeKeepExistingObject = Message.createExecute(1).createNode(); @Child Node executeFreeAndGetObject = Message.createExecute(1).createNode(); @Child Node executeFreeAndGetContent = Message.createExecute(2).createNode(); @Override public Object executeTest(VirtualFrame frame) throws InteropException { Object obj = frame.getArguments()[0]; testArg.intField = 42; Object nativePtr1 = ForeignAccess.sendExecute(executeKeepExistingObject, keepExistingObject, obj); Object nativePtr2 = ForeignAccess.sendExecute(executeKeepExistingObject, keepExistingObject, obj); Object nativePtr3 = ForeignAccess.sendExecute(executeKeepExistingObject, keepExistingObject, obj); Object ret = ForeignAccess.sendExecute(executeFreeAndGetContent, freeAndGetContent, nativeEnv, nativePtr1); assertEquals(42, (int) (Integer) ret); testArg.intField--; ret = ForeignAccess.sendExecute(executeFreeAndGetContent, freeAndGetContent, nativeEnv, nativePtr2); assertEquals(41, (int) (Integer) ret); return ForeignAccess.sendExecute(executeFreeAndGetObject, freeAndGetObject, nativePtr3); } } @Test public void testKeepObject(@Inject(TestKeepObjectNode.class) CallTarget callTarget) { testArg = new TestObject(); Object finalRet = callTarget.call(testArg); Assert.assertThat("return value", finalRet, is(instanceOf(TestObject.class))); Assert.assertSame("return value", testArg, finalRet); } public static class TestKeepNewObjectNode extends NFITestRootNode { final TruffleObject keepNewObject = lookupAndBind("keep_new_object", "(pointer):pointer"); final TruffleObject freeAndGetObject = lookupAndBind("free_and_get_object", "(pointer):object"); @Child Node executeKeepNewObject = Message.createExecute(1).createNode(); @Child Node executeFreeAndGetObject = Message.createExecute(1).createNode(); @Override public Object executeTest(VirtualFrame frame) throws InteropException { Object nativePtr = ForeignAccess.sendExecute(executeKeepNewObject, keepNewObject, nativeEnv); return ForeignAccess.sendExecute(executeFreeAndGetObject, freeAndGetObject, nativePtr); } } @Test public void testKeepNewObject(@Inject(TestKeepNewObjectNode.class) CallTarget callTarget) { Object ret = callTarget.call(); Assert.assertThat("return value", ret, is(instanceOf(TestObject.class))); Assert.assertEquals("intField", 8472, ((TestObject) ret).intField); } }