From 6fa3afd9e63a345c67a109602c92198bf6c91a8b Mon Sep 17 00:00:00 2001
From: Tomas Stupka <tomas.stupka@oracle.com>
Date: Mon, 26 Jun 2017 16:35:42 +0200
Subject: [PATCH] access foreign elements via index/name vector

---
 .../access/vector/ExtractVectorNode.java      | 192 +++++++++++-------
 .../r/runtime/interop/ForeignArray2R.java     | 131 +++++++-----
 .../truffle/r/test/ExpectedTestOutput.test    | 171 ++++++++++++++++
 .../r/test/library/fastr/TestJavaInterop.java | 125 ++++++++++++
 4 files changed, 492 insertions(+), 127 deletions(-)

diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
index e6017cd274..d246ce5719 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java
@@ -32,7 +32,6 @@ import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.KeyInfo;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.UnknownIdentifierException;
 import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
@@ -54,6 +53,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.ForeignArray2R;
+import com.oracle.truffle.r.runtime.interop.ForeignArray2R.InteropTypeCheck;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 @ImportStatic({RRuntime.class, com.oracle.truffle.api.interop.Message.class})
@@ -97,7 +98,26 @@ public abstract class ExtractVectorNode extends RBaseNode {
 
     protected abstract Object execute(VirtualFrame frame, Object vector, Object[] positions, Object exact, Object dropDimensions);
 
-    protected static boolean isForeignObject(TruffleObject object) {
+    @Specialization(guards = {"cached != null", "cached.isSupported(vector, positions)"})
+    protected Object doExtractSameDimensions(VirtualFrame frame, RAbstractVector vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("createRecursiveCache(vector, positions)") RecursiveExtractSubscriptNode cached) {
+        return cached.apply(frame, vector, positions, exact, dropDimensions);
+    }
+
+    @Specialization(guards = {"cached != null", "cached.isSupported(vector, positions)"})
+    protected Object doExtractRecursive(VirtualFrame frame, RAbstractListVector vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("createRecursiveCache(vector, positions)") RecursiveExtractSubscriptNode cached) {
+        return cached.apply(frame, vector, positions, exact, dropDimensions);
+    }
+
+    protected RecursiveExtractSubscriptNode createRecursiveCache(Object vector, Object[] positions) {
+        if (isRecursiveSubscript(vector, positions)) {
+            return RecursiveExtractSubscriptNode.create((RAbstractListVector) vector, positions[0]);
+        }
+        return null;
+    }
+
+    protected static boolean isForeignObject(Object object) {
         return RRuntime.isForeignObject(object);
     }
 
@@ -109,7 +129,90 @@ public abstract class ExtractVectorNode extends RBaseNode {
         return Foreign2RNodeGen.create();
     }
 
-    @Specialization(guards = {"isForeignObject(object)", "positions.length == cachedLength"})
+    protected boolean positionsByVector(Object[] positions) {
+        return positions.length == 1 && positions[0] instanceof RAbstractVector && ((RAbstractVector) positions[0]).getLength() > 1;
+    }
+
+    private boolean isRecursiveSubscript(Object vector, Object[] positions) {
+        return !recursive && !ignoreRecursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1;
+    }
+
+    @Specialization(limit = "CACHE_LIMIT", guards = {"!isForeignObject(vector)", "cached != null", "cached.isSupported(vector, positions, exact, dropDimensions)"})
+    protected Object doExtractDefaultCached(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("createDefaultCache(getThis(), vector, positions, exact, dropDimensions)") CachedExtractVectorNode cached) {
+        assert !isRecursiveSubscript(vector, positions);
+        return cached.apply(vector, positions, null, exact, dropDimensions);
+    }
+
+    protected static CachedExtractVectorNode createDefaultCache(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
+        return new CachedExtractVectorNode(node.getMode(), (RTypedValue) vector, positions, (RTypedValue) exact, (RTypedValue) dropDimensions, node.recursive);
+    }
+
+    @Specialization(replaces = "doExtractDefaultCached", guards = "!isForeignObject(vector)")
+    @TruffleBoundary
+    protected Object doExtractDefaultGeneric(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
+                    @Cached("new(createDefaultCache(getThis(), vector, positions, exact, dropDimensions))") GenericVectorExtractNode generic) {
+        return generic.get(this, vector, positions, exact, dropDimensions).apply(vector, positions, null, exact, dropDimensions);
+    }
+
+    // TODO hack until Truffle-DSL supports this.
+    protected ExtractVectorNode getThis() {
+        return this;
+    }
+
+    protected static final class GenericVectorExtractNode extends TruffleBoundaryNode {
+
+        @Child private CachedExtractVectorNode cached;
+
+        public GenericVectorExtractNode(CachedExtractVectorNode cachedOperation) {
+            this.cached = insert(cachedOperation);
+        }
+
+        public CachedExtractVectorNode get(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
+            CompilerAsserts.neverPartOfCompilation();
+            if (!cached.isSupported(vector, positions, exact, dropDimensions)) {
+                cached = cached.replace(createDefaultCache(node, vector, positions, exact, dropDimensions));
+            }
+            return cached;
+        }
+    }
+
+    @Specialization(guards = {"isForeignObject(object)", "positionsByVector(positions)"})
+    protected Object accessFieldByVectorPositions(TruffleObject object, Object[] positions, @SuppressWarnings("unused") Object exact, @SuppressWarnings("unused") Object dropDimensions,
+                    @Cached("READ.createNode()") Node foreignRead,
+                    @Cached("KEY_INFO.createNode()") Node keyInfoNode,
+                    @Cached("create()") CastStringNode castNode,
+                    @Cached("createFirstString()") FirstStringNode firstString,
+                    @Cached("IS_NULL.createNode()") Node isNullNode,
+                    @Cached("IS_BOXED.createNode()") Node isBoxedNode,
+                    @Cached("UNBOX.createNode()") Node unboxNode,
+                    @Cached("createForeign2RNode()") Foreign2R foreign2RNode) {
+
+        RAbstractVector vec = (RAbstractVector) positions[0];
+        Object[] resultElements = new Object[vec.getLength()];
+        InteropTypeCheck typeCheck = new InteropTypeCheck();
+
+        try {
+            for (int i = 0; i < vec.getLength(); i++) {
+                Object res = read(this, vec.getDataAtAsObject(i), foreignRead, keyInfoNode, object, firstString, castNode);
+                if (RRuntime.isForeignObject(res)) {
+                    if (ForeignAccess.sendIsNull(isNullNode, (TruffleObject) res)) {
+                        res = RNull.instance;
+                    }
+                    if (ForeignAccess.sendIsBoxed(isBoxedNode, (TruffleObject) res)) {
+                        res = ForeignAccess.sendUnbox(unboxNode, (TruffleObject) res);
+                    }
+                }
+                typeCheck.check(res);
+                resultElements[i] = foreign2RNode.execute(res);
+            }
+            return ForeignArray2R.asAbstractVector(resultElements, typeCheck);
+        } catch (InteropException | NoSuchFieldError e) {
+            throw RError.interopError(RError.findParentRBase(this), e, object);
+        }
+    }
+
+    @Specialization(guards = {"isForeignObject(object)", "!positionsByVector(positions)", "positions.length == cachedLength"})
     protected Object accessField(TruffleObject object, Object[] positions, @SuppressWarnings("unused") Object exact, @SuppressWarnings("unused") Object dropDimensions,
                     @Cached("READ.createNode()") Node foreignRead,
                     @Cached("KEY_INFO.createNode()") Node keyInfoNode,
@@ -126,20 +229,16 @@ public abstract class ExtractVectorNode extends RBaseNode {
             throw error(RError.Message.GENERIC, "No positions for foreign access.");
         }
         try {
-            try {
-                // TODO implicite unboxing ok? method calls seem to behave this way
-                Object result = object;
-                for (int i = 0; i < pos.length; i++) {
-                    result = read(this, pos[i], foreignRead, keyInfoNode, (TruffleObject) result, firstString, castNode);
-                    if (pos.length > 1 && i < pos.length - 1) {
-                        assert result instanceof TruffleObject;
-                    }
+            // TODO implicite unboxing ok? method calls seem to behave this way
+            Object result = object;
+            for (int i = 0; i < pos.length; i++) {
+                result = read(this, pos[i], foreignRead, keyInfoNode, (TruffleObject) result, firstString, castNode);
+                if (pos.length > 1 && i < pos.length - 1) {
+                    assert result instanceof TruffleObject;
                 }
-                return unbox(result, isNullNode, isBoxedNode, unboxNode, foreign2RNode);
-            } catch (UnknownIdentifierException | NoSuchFieldError e) {
-                throw RError.interopError(RError.findParentRBase(this), e, object);
             }
-        } catch (InteropException e) {
+            return unbox(result, isNullNode, isBoxedNode, unboxNode, foreign2RNode);
+        } catch (InteropException | NoSuchFieldError e) {
             throw RError.interopError(RError.findParentRBase(this), e, object);
         }
     }
@@ -190,67 +289,4 @@ public abstract class ExtractVectorNode extends RBaseNode {
     private static TruffleObject toJavaClass(TruffleObject obj) {
         return JavaInterop.toJavaClass(obj);
     }
-
-    @Specialization(guards = {"cached != null", "cached.isSupported(vector, positions)"})
-    protected Object doExtractSameDimensions(VirtualFrame frame, RAbstractVector vector, Object[] positions, Object exact, Object dropDimensions,  //
-                    @Cached("createRecursiveCache(vector, positions)") RecursiveExtractSubscriptNode cached) {
-        return cached.apply(frame, vector, positions, exact, dropDimensions);
-    }
-
-    @Specialization(guards = {"cached != null", "cached.isSupported(vector, positions)"})
-    protected Object doExtractRecursive(VirtualFrame frame, RAbstractListVector vector, Object[] positions, Object exact, Object dropDimensions,  //
-                    @Cached("createRecursiveCache(vector, positions)") RecursiveExtractSubscriptNode cached) {
-        return cached.apply(frame, vector, positions, exact, dropDimensions);
-    }
-
-    protected RecursiveExtractSubscriptNode createRecursiveCache(Object vector, Object[] positions) {
-        if (isRecursiveSubscript(vector, positions)) {
-            return RecursiveExtractSubscriptNode.create((RAbstractListVector) vector, positions[0]);
-        }
-        return null;
-    }
-
-    private boolean isRecursiveSubscript(Object vector, Object[] positions) {
-        return !recursive && !ignoreRecursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1;
-    }
-
-    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions, exact, dropDimensions)"})
-    protected Object doExtractDefaultCached(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
-                    @Cached("createDefaultCache(getThis(), vector, positions, exact, dropDimensions)") CachedExtractVectorNode cached) {
-        assert !isRecursiveSubscript(vector, positions);
-        return cached.apply(vector, positions, null, exact, dropDimensions);
-    }
-
-    protected static CachedExtractVectorNode createDefaultCache(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
-        return new CachedExtractVectorNode(node.getMode(), (RTypedValue) vector, positions, (RTypedValue) exact, (RTypedValue) dropDimensions, node.recursive);
-    }
-
-    @Specialization(replaces = "doExtractDefaultCached")
-    @TruffleBoundary
-    protected Object doExtractDefaultGeneric(Object vector, Object[] positions, Object exact, Object dropDimensions,  //
-                    @Cached("new(createDefaultCache(getThis(), vector, positions, exact, dropDimensions))") GenericVectorExtractNode generic) {
-        return generic.get(this, vector, positions, exact, dropDimensions).apply(vector, positions, null, exact, dropDimensions);
-    }
-
-    // TODO hack until Truffle-DSL supports this.
-    protected ExtractVectorNode getThis() {
-        return this;
-    }
-
-    protected static final class GenericVectorExtractNode extends TruffleBoundaryNode {
-
-        @Child private CachedExtractVectorNode cached;
-
-        public GenericVectorExtractNode(CachedExtractVectorNode cachedOperation) {
-            this.cached = insert(cachedOperation);
-        }
-
-        public CachedExtractVectorNode get(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) {
-            CompilerAsserts.neverPartOfCompilation();
-            if (!cached.isSupported(vector, positions, exact, dropDimensions)) {
-                cached = cached.replace(createDefaultCache(node, vector, positions, exact, dropDimensions));
-            }
-            return cached;
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java
index 03ef1cf845..d3999179c1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java
@@ -61,7 +61,6 @@ public abstract class ForeignArray2R extends RBaseNode {
     @Specialization(guards = {"isArray(obj, hasSize)"})
     @TruffleBoundary
     public RAbstractVector doArray(TruffleObject obj,
-                    @SuppressWarnings("unused") @Cached("HAS_SIZE.createNode()") Node hasSize,
                     @Cached("GET_SIZE.createNode()") Node getSize) {
         int size;
         try {
@@ -71,12 +70,7 @@ public abstract class ForeignArray2R extends RBaseNode {
             }
 
             CollectedElements ce = getArrayElements(size, obj);
-            RAbstractVector ret = toVector(ce);
-            if (ret != null) {
-                return ret;
-            } else {
-                return RDataFactory.createList(ce.elements);
-            }
+            return asAbstractVector(ce.elements, ce.typeCheck);
         } catch (UnsupportedMessageException | UnknownIdentifierException e) {
             throw error(RError.Message.GENERIC, "error while converting array: " + e.getMessage());
         }
@@ -89,11 +83,7 @@ public abstract class ForeignArray2R extends RBaseNode {
 
         try {
             CollectedElements ce = getIterableElements(obj, execute);
-            RAbstractVector ret = toVector(ce);
-            if (ret != null) {
-                return ret;
-            }
-            return RDataFactory.createList(ce.elements);
+            return asAbstractVector(ce.elements, ce.typeCheck);
         } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException | ArityException e) {
             throw error(RError.Message.GENERIC, "error while casting external object to list: " + e.getMessage());
         }
@@ -166,11 +156,7 @@ public abstract class ForeignArray2R extends RBaseNode {
                 }
             }
         }
-
-        ce.allBoolean &= value instanceof Boolean;
-        ce.allInteger &= value instanceof Byte || value instanceof Integer || value instanceof Short;
-        ce.allDouble &= value instanceof Double || value instanceof Float || value instanceof Long;
-        ce.allString &= value instanceof Character || value instanceof String;
+        ce.typeCheck.check(value);
 
         if (foreign2R == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -179,35 +165,45 @@ public abstract class ForeignArray2R extends RBaseNode {
         return foreign2R.execute(value);
     }
 
-    protected RAbstractVector toVector(CollectedElements ce) {
-        int size = ce.elements.length;
-        if (ce.allBoolean) {
-            byte[] ret = new byte[size];
-            for (int i = 0; i < size; i++) {
-                ret[i] = ((Number) ce.elements[i]).byteValue();
-            }
-            return RDataFactory.createLogicalVector(ret, true);
-        }
-        if (ce.allInteger) {
-            int[] ret = new int[size];
-            for (int i = 0; i < size; i++) {
-                ret[i] = ((Number) ce.elements[i]).intValue();
-            }
-            return RDataFactory.createIntVector(ret, true);
+    public static RAbstractVector asAbstractVector(Object[] elements, InteropTypeCheck typeCheck) {
+        InteropTypeCheck.RType type = typeCheck.getType();
+        if (type == null) {
+            return RDataFactory.createList(elements);
         }
-        if (ce.allDouble) {
-            double[] ret = new double[size];
-            for (int i = 0; i < size; i++) {
-                ret[i] = ((Number) ce.elements[i]).doubleValue();
-            }
-            return RDataFactory.createDoubleVector(ret, true);
-        }
-        if (ce.allString) {
-            String[] ret = new String[size];
-            for (int i = 0; i < size; i++) {
-                ret[i] = String.valueOf(ce.elements[i]);
-            }
-            return RDataFactory.createStringVector(ret, true);
+        int size = elements.length;
+        // TODO how to deal with NAs?
+        boolean complete = true;
+        switch (type) {
+            case BOOLEAN:
+                byte[] bytes = new byte[size];
+                for (int i = 0; i < size; i++) {
+                    bytes[i] = ((Number) elements[i]).byteValue();
+                    complete &= RRuntime.isNA(bytes[i]);
+                }
+                return RDataFactory.createLogicalVector(bytes, complete);
+            case DOUBLE:
+                double[] doubles = new double[size];
+                for (int i = 0; i < size; i++) {
+                    doubles[i] = ((Number) elements[i]).doubleValue();
+                    complete &= RRuntime.isNA(doubles[i]);
+                }
+                return RDataFactory.createDoubleVector(doubles, complete);
+            case INTEGER:
+                int[] ints = new int[size];
+                for (int i = 0; i < size; i++) {
+                    ints[i] = ((Number) elements[i]).intValue();
+                    complete &= RRuntime.isNA(ints[i]);
+                }
+                return RDataFactory.createIntVector(ints, complete);
+            case STRING:
+                String[] strings = new String[size];
+                for (int i = 0; i < size; i++) {
+                    strings[i] = String.valueOf(elements[i]);
+                    complete &= RRuntime.isNA(strings[i]);
+                }
+                return RDataFactory.createStringVector(strings, complete);
+            default:
+                assert false;
         }
         return null;
     }
@@ -220,11 +216,48 @@ public abstract class ForeignArray2R extends RBaseNode {
         return RRuntime.isForeignObject(obj) && JavaInterop.isJavaObject(Iterable.class, obj);
     }
 
+    public static class InteropTypeCheck {
+        public enum RType {
+            BOOLEAN,
+            DOUBLE,
+            INTEGER,
+            STRING;
+        }
+
+        private RType type = null;
+        private boolean sameRType = true;
+
+        public void check(Object value) {
+            if (value instanceof Boolean) {
+                setType(RType.BOOLEAN);
+            } else if (value instanceof Byte || value instanceof Integer || value instanceof Short) {
+                setType(RType.INTEGER);
+            } else if (value instanceof Double || value instanceof Float || value instanceof Long) {
+                setType(RType.DOUBLE);
+            } else if (value instanceof Character || value instanceof String) {
+                setType(RType.STRING);
+            } else {
+                this.type = null;
+                sameRType = false;
+            }
+        }
+
+        private void setType(RType check) {
+            if (sameRType && this.type == null) {
+                this.type = check;
+            } else if (this.type != check) {
+                this.type = null;
+                sameRType = false;
+            }
+        }
+
+        public RType getType() {
+            return sameRType ? type : null;
+        }
+    }
+
     private class CollectedElements {
         Object[] elements;
-        boolean allBoolean = true;
-        boolean allInteger = true;
-        boolean allDouble = true;
-        boolean allString = true;
+        InteropTypeCheck typeCheck = new InteropTypeCheck();
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 4e8f27c9ff..2ffa78dee1 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -131257,6 +131257,14 @@ $longValue
 #if (!any(R.version$engine == "FastR")) { "true127a32767214748364792233720368547758071.7976931348623157E3083.4028235E38testString" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to$allTypesStaticMethod(TRUE,127,"a",32767,2147483647,9223372036854775807,1.7976931348623157E308,3.4028235E38,"testString") }
 [1] "true127a32767214748364792233720368547758071.7976931348623157E3083.4028235E38testString"
 
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testArrayAsParameter#
+#if (!any(R.version$engine == "FastR")) { '[I' } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'));ja <- as.java.array(c(1L, 2L, 3L), 'int'); to$isIntArray(ja) }
+[1] "[I"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testArrayAsParameter#
+#if (!any(R.version$engine == "FastR")) { '[Ljava.lang.Integer;' } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'));ja <- as.java.array(c(1L, 2L, 3L), 'java.lang.Integer'); to$isIntegerArray(ja) }
+[1] "[Ljava.lang.Integer;"
+
 ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testArrayReadWrite#
 #if (!any(R.version$engine == "FastR")) { 1 } else { a <- as.java.array(c(1,2,3)); a[1] }
 [1] 1
@@ -132900,6 +132908,10 @@ Error in attr(to, which = "a") : external object cannot be attributed
 #if (!any(R.version$engine == "FastR")) { c('a', 'b', 'c', 'a', 'b', 'c') } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); c(to$listString, to$listString) }
 [1] "a" "b" "c" "a" "b" "c"
 
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testConvertEmptyList#Ignored.ImplementationError#
+#if (!any(R.version$engine == "FastR")) { as.character(list()) } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'));as.character(to$listEmpty); }
+character(0)
+
 ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFields#
 #if (!any(R.version$engine == "FastR")) { "a string" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to$fieldStaticStringObject }
 [1] "a string"
@@ -133152,6 +133164,128 @@ NULL
 [external object]
 [1] 1.1 2.1 3.1
 
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "character" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticCharArray); typeof(v) }
+[1] "character"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "character" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticStringArray); typeof(v) }
+[1] "character"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "double" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticDoubleArray); typeof(v) }
+[1] "double"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "double" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticFloatArray); typeof(v) }
+[1] "double"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "double" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticLongArray); typeof(v) }
+[1] "double"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "double" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$objectDoubleArray); typeof(v) }
+[1] "double"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "integer" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticByteArray); typeof(v) }
+[1] "integer"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "integer" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticIntegerArray); typeof(v) }
+[1] "integer"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "integer" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticShortArray); typeof(v) }
+[1] "integer"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "integer" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$objectIntArray); typeof(v) }
+[1] "integer"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { "logical" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticBooleanArray); typeof(v) }
+[1] "logical"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- .fastr.interop.fromArray(to$hasNullIntArray); is.list(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- .fastr.interop.fromArray(to$mixedTypesArray); is.list(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- .fastr.interop.fromArray(to$objectArray); is.list(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticBooleanArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticByteArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticCharArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticDoubleArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticFloatArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticIntegerArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticLongArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticShortArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$fieldStaticStringArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$objectDoubleArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { TRUE } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- as.vector(to$objectIntArray); is.vector(v) }
+[1] TRUE
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { list() } else { ja <- new.java.array('java.lang.String', 0L); .fastr.interop.fromArray(ja) }
+list()
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { list(1) } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- .fastr.interop.fromArray(to$hasNullIntArray); v[1] }
+[[1]]
+[1] 1
+
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { list(3) } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- .fastr.interop.fromArray(to$hasNullIntArray); v[3] }
+[[1]]
+[1] 3
+
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testFromArray#
+#if (!any(R.version$engine == "FastR")) { list(NULL) } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); v <- .fastr.interop.fromArray(to$hasNullIntArray); v[2] }
+[[1]]
+NULL
+
+
 ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testGetClass#
 #if (!any(R.version$engine == "FastR")) { 'com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass' } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass'));java.class(to) }
 [1] "com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass"
@@ -133506,6 +133640,22 @@ Error in is.infinite(to) :
 Error in is.nan(to) :
   default method not implemented for type 'external object'
 
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testMap#
+#if (!any(R.version$engine == "FastR")) { '1' } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); m <- to$map; m['one'] }
+[1] "1"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testMap#Ignored.Unimplemented#
+#if (!any(R.version$engine == "FastR")) { '11' } else { how to put into map? }
+Error: unexpected symbol in "if (!any(R.version$engine == "FastR")) { '11' } else { how to"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testMap#
+#if (!any(R.version$engine == "FastR")) { '11' } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); m <- to$map; m['one']<-'11'; m['one'] }
+[1] "11"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testMap#
+#if (!any(R.version$engine == "FastR")) { '2' } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); m <- to$map; m['two'] }
+[1] "2"
+
 ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testMethods#
 #if (!any(R.version$engine == "FastR")) { "a string" } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to$methodStaticStringObject() }
 [1] "a string"
@@ -134352,6 +134502,27 @@ Error: object 'java.lang.String' not found
 #if (!any(R.version$engine == "FastR")) { 'short' } else { toc <- new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestOverload');  toc$isOverloaded(as.external.short(1)) }
 [1] "short"
 
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testReadByVector#
+#if (!any(R.version$engine == "FastR")) { c('1', '3') } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to$map[c('one', 'three')] }
+[1] "1" "3"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testReadByVector#
+#if (!any(R.version$engine == "FastR")) { c('a string', 'a') } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to[c('fieldStringObject', 'fieldChar')] }
+[1] "a string" "a"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testReadByVector#
+#if (!any(R.version$engine == "FastR")) { c('a', 'c') } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to$fieldStringArray[c(1, 3)] }
+[1] "a" "c"
+
+##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testReadByVector#
+#if (!any(R.version$engine == "FastR")) { list('a string', 2147483647) } else { to <- new.external(new.java.class('com.oracle.truffle.r.test.library.fastr.TestJavaInterop$TestClass')); to[c('fieldStringObject', 'fieldInteger')] }
+[[1]]
+[1] "a string"
+
+[[2]]
+[1] 2147483647
+
+
 ##com.oracle.truffle.r.test.library.fastr.TestJavaInterop.testToArray#
 #if (!any(R.version$engine == "FastR")) { '[B' } else { a <- as.java.array(as.raw(1)); java.class(a); }
 [1] "[B"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java
index 6776621532..9b8c05231c 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestJavaInterop.java
@@ -196,6 +196,12 @@ public class TestJavaInterop extends TestBase {
         assertEvalFastR(Ignored.Unimplemented, "a <- as.java.array(1L,,F); a;", getRValue(new int[]{1}));
     }
 
+    @Test
+    public void testArrayAsParameter() {
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + "ja <- as.java.array(c(1L, 2L, 3L), 'int'); to$isIntArray(ja)", "'" + (new int[1]).getClass().getName() + "'");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + "ja <- as.java.array(c(1L, 2L, 3L), 'java.lang.Integer'); to$isIntegerArray(ja)", "'" + (new Integer[1]).getClass().getName() + "'");
+    }
+
     @Test
     public void testNewArray() {
         testNewArray("java.lang.Boolean", true);
@@ -268,6 +274,31 @@ public class TestJavaInterop extends TestBase {
         assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- as.vector(to$hasNullIntArray); v[3]", "list(3)");
     }
 
+    @Test
+    public void testFromArray() {
+        testAsVectorFromArray("fieldStaticBooleanArray", "logical");
+        testAsVectorFromArray("fieldStaticByteArray", "integer");
+        testAsVectorFromArray("fieldStaticCharArray", "character");
+        testAsVectorFromArray("fieldStaticDoubleArray", "double");
+        testAsVectorFromArray("fieldStaticFloatArray", "double");
+        testAsVectorFromArray("fieldStaticIntegerArray", "integer");
+        testAsVectorFromArray("fieldStaticLongArray", "double");
+        testAsVectorFromArray("fieldStaticShortArray", "integer");
+        testAsVectorFromArray("fieldStaticStringArray", "character");
+
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- .fastr.interop.fromArray(to$objectArray); is.list(v)", "TRUE");
+        testAsVectorFromArray("objectIntArray", "integer");
+        testAsVectorFromArray("objectDoubleArray", "double");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- .fastr.interop.fromArray(to$mixedTypesArray); is.list(v)", "TRUE");
+
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- .fastr.interop.fromArray(to$hasNullIntArray); is.list(v)", "TRUE");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- .fastr.interop.fromArray(to$hasNullIntArray); v[1]", "list(1)");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- .fastr.interop.fromArray(to$hasNullIntArray); v[2]", "list(NULL)");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- .fastr.interop.fromArray(to$hasNullIntArray); v[3]", "list(3)");
+
+        assertEvalFastR("ja <- new.java.array('java.lang.String', 0L); .fastr.interop.fromArray(ja)", "list()");
+    }
+
     public void testAsVectorFromArray(String field, String type) {
         assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- as.vector(to$" + field + "); is.vector(v)", "TRUE");
         assertEvalFastR(CREATE_TRUFFLE_OBJECT + " v <- as.vector(to$" + field + "); typeof(v)", getRValue(type));
@@ -441,6 +472,17 @@ public class TestJavaInterop extends TestBase {
         assertEvalFastR(CREATE_TRUFFLE_OBJECT + " to$int2DimArray[[1,2]] <- 12345L; to$int2DimArray[[1,2]]", "12345");
     }
 
+    @Test
+    public void testReadByVector() {
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " to$fieldStringArray[c(1, 3)]", "c('a', 'c')");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " to$map[c('one', 'three')]", "c('1', '3')");
+
+        TestClass tc = new TestClass();
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " to[c('fieldStringObject', 'fieldChar')]", "c('" + tc.fieldStringObject + "', '" + tc.fieldChar + "')");
+        assertEvalFastR(CREATE_TRUFFLE_OBJECT + " to[c('fieldStringObject', 'fieldInteger')]", "list('" + tc.fieldStringObject + "', " + tc.fieldInteger + ")");
+    }
+
+    @Test
     public void testMap() {
         assertEvalFastR(CREATE_TRUFFLE_OBJECT + " m <- to$map; m['one']", "'1'");
         assertEvalFastR(CREATE_TRUFFLE_OBJECT + " m <- to$map; m['two']", "'2'");
@@ -589,6 +631,11 @@ public class TestJavaInterop extends TestBase {
         assertEvalFastR(Output.IgnoreErrorContext, CREATE_TRUFFLE_OBJECT + " l<-as.list(to);", "cat('Error in as.list(to) : ', '\n', ' no method for coercing this external object to a list', '\n')");
     }
 
+    @Test
+    public void testConvertEmptyList() throws IllegalArgumentException, IllegalAccessException {
+        assertEvalFastR(Ignored.ImplementationError, CREATE_TRUFFLE_OBJECT + "as.character(to$listEmpty);", "as.character(list())");
+    }
+
     @Test
     public void testAsXXX() throws IllegalArgumentException, IllegalAccessException {
         testAsXXX("as.character");
@@ -1168,6 +1215,7 @@ public class TestJavaInterop extends TestBase {
         public List<String> listString = new ArrayList<>(Arrays.asList("a", "b", "c"));
         public List<String> listStringInt = new ArrayList<>(Arrays.asList("1", "2", "3"));
         public List<String> listStringBoolean = new ArrayList<>(Arrays.asList("TRUE", "TRUE", "FALSE"));
+        public List<String> listEmpty = new ArrayList<>();
 
         public static class Element {
             public final String data;
@@ -1255,6 +1303,7 @@ public class TestJavaInterop extends TestBase {
             map = new HashMap<>();
             map.put("one", "1");
             map.put("two", "2");
+            map.put("three", "3");
         }
 
         public static boolean methodStaticBoolean() {
@@ -1444,6 +1493,82 @@ public class TestJavaInterop extends TestBase {
         public boolean equals(TestClass tc) {
             return tc == this;
         }
+
+        public String isOverloaded(boolean b) {
+            return "boolean";
+        }
+
+        public String isOverloaded(Boolean b) {
+            return Boolean.class.getName();
+        }
+
+        public String isOverloaded(byte b) {
+            return "byte";
+        }
+
+        public String isOverloaded(Byte b) {
+            return Byte.class.getName();
+        }
+
+        public String isOverloaded(char c) {
+            return "char";
+        }
+
+        public String isOverloaded(Character c) {
+            return Character.class.getName();
+        }
+
+        public String isOverloaded(double l) {
+            return "double";
+        }
+
+        public String isOverloaded(Double l) {
+            return Double.class.getName();
+        }
+
+        public String isOverloaded(Float f) {
+            return Float.class.getName();
+        }
+
+        public String isOverloaded(float f) {
+            return "float";
+        }
+
+        public String isOverloaded(int c) {
+            return "int";
+        }
+
+        public String isOverloaded(Integer c) {
+            return Integer.class.getName();
+        }
+
+        public String isOverloaded(long l) {
+            return "long";
+        }
+
+        public String isOverloaded(Long l) {
+            return Long.class.getName();
+        }
+
+        public String isOverloaded(short c) {
+            return "short";
+        }
+
+        public String isOverloaded(Short c) {
+            return Short.class.getName();
+        }
+
+        public String isOverloaded(String s) {
+            return String.class.getName();
+        }
+
+        public String isIntArray(int[] a) {
+            return a.getClass().getName();
+        }
+
+        public String isIntegerArray(Integer[] a) {
+            return a.getClass().getName();
+        }
     }
 
     public static class TestArrayClass {
-- 
GitLab