From 54c1b7ac730b1d5f97c1452d008cc8954273efe2 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Wed, 2 Aug 2017 10:32:35 +0200
Subject: [PATCH] prototype handling of character vectors in NFI .C

---
 .../truffle/r/ffi/impl/nfi/TruffleNFI_C.java  | 126 +++++++++++++++++-
 .../oracle/truffle/r/runtime/ffi/CRFFI.java   |  11 +-
 2 files changed, 129 insertions(+), 8 deletions(-)

diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
index 2913e0e1b7..54e3f8b3a3 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
@@ -22,25 +22,147 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
+import java.nio.charset.StandardCharsets;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.CanResolve;
 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.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
 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.r.ffi.impl.interop.UnsafeAdapter;
 import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_CFactory.TruffleNFI_InvokeCNodeGen;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
+import sun.misc.Unsafe;
+
 public class TruffleNFI_C implements CRFFI {
+
+    @MessageResolution(receiverType = StringWrapper.class)
+    public static class StringWrapperMR {
+
+        @Resolve(message = "IS_POINTER")
+        public abstract static class StringWrapperNativeIsPointerNode extends Node {
+            protected Object access(@SuppressWarnings("unused") StringWrapper receiver) {
+                return true;
+            }
+        }
+
+        @Resolve(message = "AS_POINTER")
+        public abstract static class StringWrapperNativeAsPointerNode extends Node {
+            protected Object access(StringWrapper receiver) {
+                return receiver.asPointer();
+            }
+        }
+
+        @CanResolve
+        public abstract static class StringWrapperCheck extends Node {
+
+            protected static boolean test(TruffleObject receiver) {
+                return receiver instanceof StringWrapper;
+            }
+        }
+    }
+
+    public static final class StringWrapper implements TruffleObject {
+
+        private final RAbstractStringVector vector;
+        private long address;
+
+        public StringWrapper(RAbstractStringVector vector) {
+            this.vector = vector;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return StringWrapperMRForeign.ACCESS;
+        }
+
+        public long asPointer() {
+            if (address == 0) {
+                address = allocate();
+            }
+            return address;
+        }
+
+        @TruffleBoundary
+        private long allocate() {
+            int length = vector.getLength();
+            int size = length * 8;
+            byte[][] bytes = new byte[length][];
+            for (int i = 0; i < length; i++) {
+                String element = vector.getDataAt(i);
+                bytes[i] = element.getBytes(StandardCharsets.US_ASCII);
+                size += bytes[i].length + 1;
+            }
+            long memory = UnsafeAdapter.UNSAFE.allocateMemory(size);
+            long ptr = memory + length * 8; // start of the actual character data
+            for (int i = 0; i < length; i++) {
+                UnsafeAdapter.UNSAFE.putLong(memory + i * 8, ptr);
+                UnsafeAdapter.UNSAFE.copyMemory(bytes[i], Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, bytes[i].length);
+                ptr += bytes[i].length;
+                UnsafeAdapter.UNSAFE.putByte(ptr++, (byte) 0);
+            }
+            assert ptr == memory + size : "should have filled everything";
+            return memory;
+        }
+
+        public RAbstractStringVector copyBack(RAbstractStringVector original) {
+            if (address == 0) {
+                return original;
+            } else {
+                RStringVector result = original.materialize();
+                String[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy();
+                for (int i = 0; i < data.length; i++) {
+                    long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8);
+                    int length = 0;
+                    while (UnsafeAdapter.UNSAFE.getByte(ptr + length) != 0) {
+                        length++;
+                    }
+                    byte[] bytes = new byte[length];
+                    UnsafeAdapter.UNSAFE.copyMemory(null, ptr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length);
+                    data[i] = new String(bytes, StandardCharsets.US_ASCII);
+                }
+                UnsafeAdapter.UNSAFE.freeMemory(address);
+                return RDataFactory.createStringVector(data, true);
+            }
+        }
+    }
+
     abstract static class TruffleNFI_InvokeCNode extends InvokeCNode {
 
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
+        @Override
+        protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) {
+            if (type == ArgumentType.VECTOR_STRING) {
+                return new StringWrapper((RAbstractStringVector) vector);
+            } else {
+                return super.getNativeArgument(index, type, vector);
+            }
+        }
+
+        @Override
+        protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) {
+            if (type == ArgumentType.VECTOR_STRING) {
+                return ((StringWrapper) nativeArgument).copyBack((RAbstractStringVector) vector);
+            } else {
+                return super.postProcessArgument(type, vector, nativeArgument);
+            }
+        }
+
         @Specialization(guards = "args.length == 0")
         protected void invokeCall0(NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args, @SuppressWarnings("unused") boolean hasStrings,
                         @Cached("createExecute(args.length)") Node executeNode) {
@@ -84,8 +206,8 @@ public class TruffleNFI_C implements CRFFI {
                 sb.append("[sint32]");
             } else if (arg instanceof double[]) {
                 sb.append("[double]");
-            } else if (arg instanceof byte[][]) {
-                sb.append("[pointer]");
+            } else if (arg instanceof StringWrapper) {
+                sb.append("pointer");
             } else {
                 throw RInternalError.unimplemented(".C type: " + arg.getClass().getSimpleName());
             }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
index 5143ea58df..80d1e70c87 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
@@ -40,7 +40,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
@@ -50,7 +49,7 @@ public interface CRFFI {
 
     public static abstract class InvokeCNode extends RBaseNode {
 
-        enum ArgumentType {
+        public enum ArgumentType {
             VECTOR_DOUBLE,
             VECTOR_INT,
             VECTOR_LOGICAL,
@@ -71,7 +70,7 @@ public interface CRFFI {
         protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings);
 
         @TruffleBoundary
-        private Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) {
+        protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) {
             CompilerAsserts.neverPartOfCompilation();
             switch (type) {
                 case VECTOR_DOUBLE: {
@@ -121,7 +120,7 @@ public interface CRFFI {
             }
         }
 
-        protected Object[] getNativeArguments(Object[] array, ArgumentType[] argTypes) {
+        private Object[] getNativeArguments(Object[] array, ArgumentType[] argTypes) {
             Object[] nativeArgs = new Object[array.length];
             for (int i = 0; i < array.length; i++) {
                 nativeArgs[i] = getNativeArgument(i, argTypes[i], (RAbstractAtomicVector) array[i]);
@@ -130,7 +129,7 @@ public interface CRFFI {
         }
 
         @TruffleBoundary
-        private static Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) {
+        protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) {
             switch (type) {
                 case VECTOR_STRING:
                     return ((RAbstractStringVector) vector).materialize().copyResetData(decodeStrings((byte[][]) nativeArgument));
@@ -151,7 +150,7 @@ public interface CRFFI {
             }
         }
 
-        protected Object[] postProcessArguments(Object[] array, ArgumentType[] argTypes, Object[] nativeArgs) {
+        private Object[] postProcessArguments(Object[] array, ArgumentType[] argTypes, Object[] nativeArgs) {
             Object[] results = new Object[array.length];
             for (int i = 0; i < array.length; i++) {
                 results[i] = postProcessArgument(argTypes[i], (RAbstractAtomicVector) array[i], nativeArgs[i]);
-- 
GitLab