From 71251f6c1b6cd24a1926eb21c676626d0c8edc01 Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Mon, 20 Apr 2015 16:35:27 -0700
Subject: [PATCH] basic fix for serializing scalar strings

---
 .../builtin/base/ConnectionFunctions.java     | 10 ++
 .../oracle/truffle/r/runtime/RDeparse.java    | 17 ++++
 .../oracle/truffle/r/runtime/RSerialize.java  | 36 +++++---
 .../r/runtime/conn/TextConnections.java       | 24 +++--
 .../truffle/r/runtime/gnur/SEXPTYPE.java      | 92 ++++++++++++-------
 5 files changed, 124 insertions(+), 55 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
index accd564d49..1344948d94 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
@@ -151,12 +151,22 @@ public abstract class ConnectionFunctions {
         @TruffleBoundary
         protected Object textConnection(RAbstractStringVector nm, RAbstractStringVector object, RAbstractStringVector open, REnvironment env, @SuppressWarnings("unused") RIntVector encoding) {
             controlVisibility();
+            if (nm.getLength() != 1) {
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "description");
+            }
+            // TODO more error checking as per GnuR
             try {
                 return new TextRConnection(nm.getDataAt(0), object, env, open.getDataAt(0));
             } catch (IOException ex) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+
+        @SuppressWarnings("unused")
+        @Fallback
+        protected Object textConnection(Object nm, Object object, Object open, Object env, Object encoding) {
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
+        }
     }
 
     @RBuiltin(name = "textConnectionValue", kind = INTERNAL, parameterNames = {"con"})
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index 75c26ad533..9b25f060d6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -721,6 +721,7 @@ public class RDeparse {
             case REALSXP:
             case CPLXSXP:
             case RAWSXP:
+                obj = checkScalarVector(obj);
                 vector2buff(state, (RVector) obj);
                 break;
 
@@ -760,6 +761,22 @@ public class RDeparse {
         }
     }
 
+    private static RVector checkScalarVector(Object obj) {
+        if (obj instanceof String) {
+            return RDataFactory.createStringVectorFromScalar((String) obj);
+        } else if (obj instanceof Byte) {
+            return RDataFactory.createLogicalVectorFromScalar((Byte) obj);
+        } else if (obj instanceof Integer) {
+            return RDataFactory.createIntVectorFromScalar((Integer) obj);
+        } else if (obj instanceof Double) {
+            return RDataFactory.createDoubleVectorFromScalar((Double) obj);
+        } else if (obj instanceof RComplex) {
+            return RDataFactory.createComplexVectorFromScalar((RComplex) obj);
+        } else {
+            return (RVector) obj;
+        }
+    }
+
     @SuppressWarnings("unused")
     private static boolean curlyahead(Object obj) {
         return false;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index bbd989a72a..5fcc530c9a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -1073,20 +1073,16 @@ public class RSerialize {
                 stream.writeInt(flags);
                 switch (type) {
                     case STRSXP: {
-                        RStringVector vec = (RStringVector) obj;
-                        stream.writeInt(vec.getLength());
-                        for (int i = 0; i < vec.getLength(); i++) {
-                            writeItem(vec.getDataAt(i));
-                        }
-                        break;
-                    }
-
-                    case CHARSXP: {
-                        String s = (String) obj;
-                        if (s == RRuntime.STRING_NA) {
-                            stream.writeInt(-1);
+                        if (obj instanceof String) {
+                            // length 1 vector
+                            stream.writeInt(1);
+                            writeCHARSXP((String) obj);
                         } else {
-                            stream.writeString(s);
+                            RStringVector vec = (RStringVector) obj;
+                            stream.writeInt(vec.getLength());
+                            for (int i = 0; i < vec.getLength(); i++) {
+                                writeCHARSXP(vec.getDataAt(i));
+                            }
                         }
                         break;
                     }
@@ -1229,6 +1225,20 @@ public class RSerialize {
             }
         }
 
+        /**
+         * Write the element of a STRSXP. We can't call {@link #writeItem} because that always
+         * treats a {@code String} as an STRSXP.
+         */
+        private void writeCHARSXP(String s) throws IOException {
+            int flags = Flags.packFlags(SEXPTYPE.CHARSXP, 0, false, false, false);
+            stream.writeInt(flags);
+            if (s == RRuntime.STRING_NA) {
+                stream.writeInt(-1);
+            } else {
+                stream.writeString(s);
+            }
+        }
+
         private void writeAttributes(RAttributes attributes) throws IOException {
             // have to convert to GnuR pairlist
             Iterator<RAttribute> iter = attributes.iterator();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java
index 7e3136db34..7551bb4ad0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/TextConnections.java
@@ -37,13 +37,13 @@ import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 
 public class TextConnections {
     public static class TextRConnection extends BaseRConnection {
-        protected String nm;
+        protected String description;
         protected RAbstractStringVector object;
         protected REnvironment env;
 
-        public TextRConnection(String nm, RAbstractStringVector object, REnvironment env, String modeString) throws IOException {
+        public TextRConnection(String description, RAbstractStringVector object, REnvironment env, String modeString) throws IOException {
             super(ConnectionClass.Text, modeString, AbstractOpenMode.Read);
-            this.nm = nm;
+            this.description = description;
             this.object = object;
             this.env = env;
             openNonLazyConnection();
@@ -51,7 +51,7 @@ public class TextConnections {
 
         @Override
         public String getSummaryDescription() {
-            return nm;
+            return description;
         }
 
         @Override
@@ -152,16 +152,21 @@ public class TextConnections {
     private static class TextWriteRConnection extends DelegateWriteRConnection implements GetConnectionValue {
         private String incompleteLine;
         private RStringVector textVec;
+        private String idName;
 
         private void initTextVec(RStringVector v, TextRConnection textBase) {
+            if (textBase.description.equals("NULL")) {
+                throw RError.nyi(null, "anonymous text output connection");
+            }
+            idName = textBase.object.getDataAt(0);
             try {
                 textVec = v;
-                textBase.env.put(textBase.nm, textVec);
+                textBase.env.put(idName, textVec);
             } catch (PutException ex) {
                 throw RError.error((SourceSection) null, ex);
             }
             // lock the binding
-            textBase.env.lockBinding(textBase.nm);
+            textBase.env.lockBinding(idName);
         }
 
         protected TextWriteRConnection(BaseRConnection base) {
@@ -179,7 +184,7 @@ public class TextConnections {
         public void closeAndDestroy() throws IOException {
             base.closed = true;
             TextRConnection textBase = (TextRConnection) base;
-            textBase.env.unlockBinding(textBase.nm);
+            textBase.env.unlockBinding(idName);
         }
 
         @Override
@@ -222,7 +227,7 @@ public class TextConnections {
                 }
                 // TODO: not thread safe
                 TextRConnection textBase = (TextRConnection) base;
-                textBase.env.unlockBinding(textBase.nm);
+                textBase.env.unlockBinding(idName);
                 // TODO: is vector really complete?
                 initTextVec(RDataFactory.createStringVector(updateData, RDataFactory.COMPLETE_VECTOR), textBase);
             }
@@ -234,8 +239,7 @@ public class TextConnections {
 
         @Override
         public void writeString(String s, boolean nl) throws IOException {
-            // TODO Auto-generated method stub
-
+            throw RError.nyi(null, "writeString on text connection");
         }
 
         @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
index 51e0dd5316..7c4c7e3af1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
@@ -29,47 +29,48 @@ public enum SEXPTYPE {
     ENVSXP(4, REnvironment.class), /* environments */
     PROMSXP(5, RPromise.class), /* promises: [un]evaluated closure arguments */
     LANGSXP(6, RLanguage.class), /* language constructs (special lists) */
-    SPECIALSXP(7, null), /* special forms */
-    BUILTINSXP(8, null), /* builtin non-special forms */
-    CHARSXP(9, String.class), /* "scalar" string type (internal only) */
+    SPECIALSXP(7), /* special forms */
+    BUILTINSXP(8), /* builtin non-special forms */
+    CHARSXP(9), /* "scalar" string type (GnuR internal only) */
     LGLSXP(10, RLogicalVector.class), /* logical vectors */
     INTSXP(13, RIntVector.class), /* integer vectors */
     REALSXP(14, RDoubleVector.class), /* real variables */
     CPLXSXP(15, RComplexVector.class), /* complex variables */
-    STRSXP(16, RStringVector.class), /* string vectors */
+    STRSXP(16, new Class<?>[]{RStringVector.class, String.class}), /* string vectors */
     DOTSXP(17, RPairList.class), /* dot-dot-dot object */
-    ANYSXP(18, null), /* make "any" args work */
+    ANYSXP(18), /* make "any" args work */
     VECSXP(19, RList.class), /* generic vectors */
     EXPRSXP(20, RExpression.class), /* expressions vectors */
-    BCODESXP(21, null), /* byte code */
-    EXTPTRSXP(22, null), /* external pointer */
-    WEAKREFSXP(23, null), /* weak reference */
+    BCODESXP(21), /* byte code */
+    EXTPTRSXP(22), /* external pointer */
+    WEAKREFSXP(23), /* weak reference */
     RAWSXP(24, RRawVector.class), /* raw bytes */
-    S4SXP(25, null), /* S4 non-vector */
+    S4SXP(25), /* S4 non-vector */
 
-    NEWSXP(30, null), /* fresh node created in new page */
-    FREESXP(31, null), /* node released by GC */
+    NEWSXP(30), /* fresh node created in new page */
+    FREESXP(31), /* node released by GC */
 
     FUNSXP(99, RFunction.class), /* Closure or Builtin */
 
     // used in RSerialize
-    REFSXP(255, null),
-    NILVALUE_SXP(254, null),
-    GLOBALENV_SXP(253, null),
-    UNBOUNDVALUE_SXP(252, null),
+    REFSXP(255),
+    NILVALUE_SXP(254),
+    GLOBALENV_SXP(253),
+    UNBOUNDVALUE_SXP(252),
     MISSINGARG_SXP(251, RMissing.class),
-    BASENAMESPACE_SXP(250, null),
-    NAMESPACESXP(249, null),
-    PACKAGESXP(248, null),
-    PERSISTSXP(247, null),
-    BCREPDEF(244, null),
-    BCREPREF(243, null),
-    EMPTYENV_SXP(242, null),
-    BASEENV_SXP(241, null),
-    ATTRLANGSXP(240, null),
-    ATTRLISTSXP(239, null),
+    BASENAMESPACE_SXP(250),
+    NAMESPACESXP(249),
+    PACKAGESXP(248),
+    PERSISTSXP(247),
+    BCREPDEF(244),
+    BCREPREF(243),
+    EMPTYENV_SXP(242),
+    BASEENV_SXP(241),
+    ATTRLANGSXP(240),
+    ATTRLISTSXP(239),
 
     // FastR scalar variants of GnuR vector types
+    // TODO remove these
     FASTR_DOUBLE(300, Double.class),
     FASTR_INT(301, Integer.class),
     FASTR_BYTE(302, Byte.class),
@@ -79,13 +80,23 @@ public enum SEXPTYPE {
     FASTR_SOURCESECTION(306, SourceSection.class);
 
     public final int code;
-    public final Class<?> fastRClass;
+    public final Class<?>[] fastRClasses;
 
-    @CompilationFinal private static final SEXPTYPE[] VALUES = values();
+    @CompilationFinal private static final SEXPTYPE[] NON_NULL_VALUES;
 
-    SEXPTYPE(int code, Class<?> fastRClass) {
+    SEXPTYPE(int code) {
         this.code = code;
-        this.fastRClass = fastRClass;
+        this.fastRClasses = null;
+    }
+
+    SEXPTYPE(int code, Class<?>[] fastRClasses) {
+        this.code = code;
+        this.fastRClasses = fastRClasses;
+
+    }
+
+    SEXPTYPE(int code, Class<?> fastRClass) {
+        this(code, new Class<?>[]{fastRClass});
     }
 
     private static final Map<Integer, SEXPTYPE> codeMap = new HashMap<>();
@@ -100,9 +111,15 @@ public enum SEXPTYPE {
      * {@code type} field on the {@link RPairList} has to be consulted.
      */
     public static SEXPTYPE typeForClass(Class<?> fastRClass) {
-        for (SEXPTYPE type : VALUES) {
-            if (fastRClass == type.fastRClass) {
-                return type;
+        for (SEXPTYPE type : NON_NULL_VALUES) {
+            if (type.fastRClasses.length == 1) {
+                if (fastRClass == type.fastRClasses[0]) {
+                    return type;
+                }
+            } else {
+                if (fastRClass == type.fastRClasses[0] || fastRClass == type.fastRClasses[1]) {
+                    return type;
+                }
             }
         }
         throw RInternalError.shouldNotReachHere(fastRClass.getName());
@@ -137,8 +154,19 @@ public enum SEXPTYPE {
     }
 
     static {
+        int nonNullCount = 0;
         for (SEXPTYPE type : SEXPTYPE.values()) {
             SEXPTYPE.codeMap.put(type.code, type);
+            if (type.fastRClasses != null) {
+                nonNullCount++;
+            }
+        }
+        NON_NULL_VALUES = new SEXPTYPE[nonNullCount];
+        nonNullCount = 0;
+        for (SEXPTYPE type : SEXPTYPE.values()) {
+            if (type.fastRClasses != null) {
+                NON_NULL_VALUES[nonNullCount++] = type;
+            }
         }
     }
 
-- 
GitLab