diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
index f2e44f0a2f31d16d490898e7b07345d87f01b01e..e793668a8a24f91d57eb57e120592cb04b223961 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
@@ -27,9 +27,11 @@ import java.io.IOException;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -38,26 +40,29 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class C_ParseRd extends RExternalBuiltinNode.Arg9 {
 
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg(0).defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
+    }
+
     @Specialization
-    protected Object parseRd(RConnection con, REnvironment srcfile, String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL,
-                    byte macrosL, byte warndupsL) {
+    protected Object parseRd(int con, REnvironment srcfile, String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL, byte macrosL, byte warndupsL) {
         return doParseRd(con, srcfile, encoding, verboseL, basename, fragmentL, warningCallsL, RDataFactory.createLogicalVectorFromScalar(macrosL), warndupsL);
     }
 
     @Specialization
-    protected Object parseRd(RConnection con, REnvironment srcfile, String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL,
-                    REnvironment macros, byte warndupsL) {
+    protected Object parseRd(int con, REnvironment srcfile, String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL, REnvironment macros, byte warndupsL) {
         return doParseRd(con, srcfile, encoding, verboseL, basename, fragmentL, warningCallsL, macros, warndupsL);
     }
 
     @TruffleBoundary
-    private Object doParseRd(RConnection con, REnvironment srcfile, @SuppressWarnings("unused") String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL,
+    private Object doParseRd(int con, REnvironment srcfile, @SuppressWarnings("unused") String encoding, byte verboseL, RAbstractStringVector basename, byte fragmentL, byte warningCallsL,
                     Object macros, byte warndupsL) {
         if (RRuntime.isNA(warningCallsL)) {
             throw RError.error(this, RError.Message.INVALID_ARGUMENT, "warningCalls");
         }
 
-        try (RConnection openConn = con.forceOpen("r")) {
+        try (RConnection openConn = RConnection.fromIndex(con).forceOpen("r")) {
             // @formatter:off
             return RFFIFactory.getRFFI().getToolsRFFI().parseRd(openConn, srcfile,
                             RDataFactory.createLogicalVectorFromScalar(verboseL),
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java
index 1e42cb01f00216e80f3a2ff768b76e02ac366dea..d42310206f651776710b9f9c9bb05d038a5c0f37 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java
@@ -294,7 +294,7 @@ public final class CountFields extends RExternalBuiltinNode {
     @TruffleBoundary
     public Object call(RArgsValuesAndNames args) {
         Object[] argValues = args.getArguments();
-        RConnection conn = (RConnection) argValues[0];
+        int conn = castInt(castVector(argValues[0]));
         Object sepArg = argValues[1];
         char sepChar;
         Object quoteArg = argValues[2];
@@ -340,7 +340,7 @@ public final class CountFields extends RExternalBuiltinNode {
                 quoteSet = s;
             }
         }
-        try (RConnection openConn = conn.forceOpen("r")) {
+        try (RConnection openConn = RConnection.fromIndex(conn).forceOpen("r")) {
             return countFields(openConn, sepChar, quoteSet, nskip, RRuntime.fromLogical(blskip), comChar);
         } catch (IllegalStateException | IOException ex) {
             errorProfile.enter();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java
index aa29bd8250b4fff99442919f9eb02e0ea8667438..1db6100ba110523fc7cfe097d94b81480a216580 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java
@@ -43,13 +43,14 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 @RBuiltin(name = "asCharacterFactor", kind = INTERNAL, parameterNames = "x", behavior = PURE)
 public abstract class AsCharacterFactor extends RBuiltinNode {
     private final NACheck naCheck = NACheck.create();
+    private static final RStringVector CLASS_FACTOR_VEC = RDataFactory.createStringVectorFromScalar(RRuntime.CLASS_FACTOR);
 
     @Child InheritsNode inheritsNode = InheritsNodeGen.create();
     @Child CastToVectorNode castToVectorNode = CastToVectorNode.create();
 
     @Specialization
     protected RStringVector doAsCharacterFactor(Object x) {
-        byte isFactor = (byte) inheritsNode.executeObject(x, RRuntime.CLASS_FACTOR, false);
+        byte isFactor = (byte) inheritsNode.execute(x, CLASS_FACTOR_VEC, false);
         if (isFactor == RRuntime.LOGICAL_FALSE) {
             throw RError.error(RError.SHOW_CALLER, RError.Message.COERCE_NON_FACTOR);
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
index 77b77b31f14c03e1309d409685310217626fc0b5..9bc2ad81ddf43cbbfe2b04094e1653a25240a0ea 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
@@ -30,7 +30,6 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -58,10 +57,4 @@ public abstract class AsInteger extends RBuiltinNode {
             return (RAbstractIntVector) v.copyDropAttributes();
         }
     }
-
-    @Specialization
-    protected int asInteger(RConnection conn) {
-        return conn.getDescriptor();
-    }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
index 3694e0b31197c471216607ab32024188273268f6..e732b61f4047b5aec05e05981f3a1521bf68f8fd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
@@ -24,13 +24,13 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asBoolean;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asInteger;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.atomicLogicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.emptyStringVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gt0;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.atomicLogicalValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.emptyStringVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
@@ -78,6 +78,8 @@ public abstract class Cat extends RBuiltinNode {
     protected void createCasts(CastBuilder casts) {
         casts.arg("sep").mustBe(stringValue(), RError.Message.INVALID_SEP);
 
+        casts.arg("file").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
+
         casts.arg("fill").mustBe(numericValue()).asVector().mustBe(singleElement()).findFirst().shouldBe(
                         instanceOf(Byte.class).or(instanceOf(Integer.class).and(gt0())),
                         Message.NON_POSITIVE_FILL).mapIf(atomicLogicalValue(), asBoolean(), asInteger());
@@ -90,26 +92,27 @@ public abstract class Cat extends RBuiltinNode {
 
     @Specialization
     @TruffleBoundary
-    protected RNull cat(RList args, RConnection conn, RAbstractStringVector sepVec, boolean fill, RAbstractStringVector labels, boolean append) {
+    protected RNull cat(RList args, int file, RAbstractStringVector sepVec, boolean fill, RAbstractStringVector labels, boolean append) {
         int fillWidth = -1;
         if (fill) {
             fillWidth = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("width"));
         }
-        return output(args, conn, sepVec, fillWidth, labels, append);
+        return output(args, file, sepVec, fillWidth, labels, append);
     }
 
     @TruffleBoundary
     @Specialization
-    protected RNull cat(RList args, RConnection conn, RAbstractStringVector sepVec, int givenFillWidth, RAbstractStringVector labels, boolean append) {
+    protected RNull cat(RList args, int file, RAbstractStringVector sepVec, int givenFillWidth, RAbstractStringVector labels, boolean append) {
         int fillWidth = -1;
         if (givenFillWidth >= 1) {
             fillWidth = givenFillWidth;
         }
-        return output(args, conn, sepVec, fillWidth, labels, append);
+        return output(args, file, sepVec, fillWidth, labels, append);
     }
 
     @TruffleBoundary
-    private RNull output(RList args, RConnection conn, RAbstractStringVector sepVec, int fillWidth, RAbstractStringVector labels, @SuppressWarnings("unused") boolean append) {
+    private RNull output(RList args, int file, RAbstractStringVector sepVec, int fillWidth, RAbstractStringVector labels, @SuppressWarnings("unused") boolean append) {
+        RConnection conn = RConnection.fromIndex(file);
         boolean filling = fillWidth > 0;
         ensureToString();
         /*
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 be8f012198999e4518b8cf0a5cde44c92d86c246..cb19879c50ceef6107f3cf71560eee880ad4a2a2 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
@@ -22,12 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asIntegerVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.equalTo;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.findFirst;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalTrue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.rawValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
@@ -59,6 +63,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.ConnectionFunctionsFactory.WriteDataNodeGen;
 import com.oracle.truffle.r.nodes.builtin.casts.fluent.HeadPhaseBuilder;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -106,8 +111,8 @@ public abstract class ConnectionFunctions {
     public abstract static class Stdin extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
-        protected RConnection stdin() {
-            return getStdin();
+        protected RAbstractIntVector stdin() {
+            return getStdin().asVector();
         }
     }
 
@@ -115,8 +120,8 @@ public abstract class ConnectionFunctions {
     public abstract static class Stdout extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
-        protected RConnection stdout() {
-            return getStdout();
+        protected RAbstractIntVector stdout() {
+            return getStdout().asVector();
         }
     }
 
@@ -124,12 +129,12 @@ public abstract class ConnectionFunctions {
     public abstract static class Stderr extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
-        protected RConnection stderr() {
-            return getStderr();
+        protected RAbstractIntVector stderr() {
+            return getStderr().asVector();
         }
     }
 
-    private static final class Casts {
+    public static final class Casts {
         private static void description(CastBuilder casts) {
             casts.arg("description").mustBe(stringValue()).asStringVector().shouldBe(singleElement(), RError.Message.ARGUMENT_ONLY_FIRST_1, "description").findFirst().notNA();
         }
@@ -150,8 +155,8 @@ public abstract class ConnectionFunctions {
             casts.arg("blocking").asLogicalVector().findFirst().notNA().map(toBoolean());
         }
 
-        private static void connection(CastBuilder casts) {
-            casts.arg("con").mustBe(instanceOf(RConnection.class));
+        public static void connection(CastBuilder casts) {
+            casts.arg("con").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
         }
 
         private static void nchars(CastBuilder casts) {
@@ -162,6 +167,17 @@ public abstract class ConnectionFunctions {
             casts.arg("useBytes").asLogicalVector().findFirst().notNA().map(toBoolean());
         }
 
+        private static void n(CastBuilder casts) {
+            casts.arg("n").asIntegerVector().findFirst().mustBe(gte(0));
+        }
+
+        private static void size(CastBuilder casts) {
+            casts.arg("size").asIntegerVector().findFirst();
+        }
+
+        private static void swap(CastBuilder casts) {
+            casts.arg("swap").asLogicalVector().findFirst().notNA().map(toBoolean());
+        }
     }
 
     @RBuiltin(name = "file", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding", "method", "raw"}, behavior = IO)
@@ -180,7 +196,7 @@ public abstract class ConnectionFunctions {
         @Specialization
         @TruffleBoundary
         @SuppressWarnings("unused")
-        protected Object file(String description, String openArg, boolean blocking, String encoding, String method, boolean raw) {
+        protected RAbstractIntVector file(String description, String openArg, boolean blocking, String encoding, String method, boolean raw) {
             String open = openArg;
             // TODO handle http/ftp prefixes and redirect and method
             String path = removeFileURLPrefix(description);
@@ -196,7 +212,7 @@ public abstract class ConnectionFunctions {
                 }
             }
             try {
-                return new FileRConnection(path, open);
+                return new FileRConnection(path, open).asVector();
             } catch (IOException ex) {
                 RError.warning(this, RError.Message.CANNOT_OPEN_FILE, description, ex.getMessage());
                 throw RError.error(this, RError.Message.CANNOT_OPEN_CONNECTION);
@@ -223,13 +239,13 @@ public abstract class ConnectionFunctions {
         @Specialization
         @TruffleBoundary
         @SuppressWarnings("unused")
-        protected Object gzFile(RAbstractStringVector description, String open, RAbstractStringVector encoding, int compression) {
+        protected RAbstractIntVector gzFile(RAbstractStringVector description, String open, RAbstractStringVector encoding, int compression) {
             try {
-                return new GZIPRConnection(description.getDataAt(0), open);
+                return new GZIPRConnection(description.getDataAt(0), open).asVector();
             } catch (ZipException ex) {
                 // wasn't a gzip file, try uncompressed text
                 try {
-                    return new FileRConnection(description.getDataAt(0), "r");
+                    return new FileRConnection(description.getDataAt(0), "r").asVector();
                 } catch (IOException ex1) {
                     throw reportError(description.getDataAt(0), ex1);
                 }
@@ -268,7 +284,7 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object textConnection(String description, Object text, String open, REnvironment env, @SuppressWarnings("unused") int encoding) {
+        protected RAbstractIntVector textConnection(String description, Object text, String open, REnvironment env, @SuppressWarnings("unused") int encoding) {
             RAbstractStringVector object;
             if (open.length() == 0 || open.equals("r")) {
                 if (text == RNull.instance) {
@@ -284,7 +300,7 @@ public abstract class ConnectionFunctions {
                 }
             }
             try {
-                return new TextRConnection(description, object, env, open);
+                return new TextRConnection(description, object, env, open).asVector();
             } catch (IOException ex) {
                 throw RInternalError.shouldNotReachHere();
             }
@@ -296,13 +312,18 @@ public abstract class ConnectionFunctions {
     public abstract static class TextConnectionValue extends RBuiltinNode {
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.arg("con").mustBe(instanceOf(TextRConnection.class), RError.Message.NOT_A_TEXT_CONNECTION);
+            casts.arg("con").defaultError(Message.NOT_A_TEXT_CONNECTION).mustNotBeNull().mustBe(integerValue()).asIntegerVector().findFirst();
         }
 
         @Specialization
         @TruffleBoundary
-        protected Object textConnection(TextRConnection conn) {
-            return RDataFactory.createStringVector(conn.getValue(), RDataFactory.COMPLETE_VECTOR);
+        protected Object textConnection(int con) {
+            RConnection connection = RConnection.fromIndex(con);
+            if (connection instanceof TextRConnection) {
+                return RDataFactory.createStringVector(((TextRConnection) connection).getValue(), RDataFactory.COMPLETE_VECTOR);
+            } else {
+                throw RError.error(RError.SHOW_CALLER, Message.NOT_A_TEXT_CONNECTION);
+            }
         }
     }
 
@@ -321,13 +342,13 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object socketConnection(String host, int port, boolean server, boolean blocking, String open,
+        protected RAbstractIntVector socketConnection(String host, int port, boolean server, boolean blocking, String open,
                         @SuppressWarnings("unused") RAbstractStringVector encoding, int timeout) {
             try {
                 if (server) {
-                    return new RSocketConnection(open, true, host, port, blocking, timeout);
+                    return new RSocketConnection(open, true, host, port, blocking, timeout).asVector();
                 } else {
-                    return new RSocketConnection(open, false, host, port, blocking, timeout);
+                    return new RSocketConnection(open, false, host, port, blocking, timeout).asVector();
                 }
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.CANNOT_OPEN_CONNECTION);
@@ -348,9 +369,9 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object urlConnection(String url, String open, @SuppressWarnings("unused") boolean blocking, @SuppressWarnings("unused") String encoding) {
+        protected RAbstractIntVector urlConnection(String url, String open, @SuppressWarnings("unused") boolean blocking, @SuppressWarnings("unused") String encoding) {
             try {
-                return new URLRConnection(url, open);
+                return new URLRConnection(url, open).asVector();
             } catch (MalformedURLException ex) {
                 throw RError.error(this, RError.Message.UNSUPPORTED_URL_SCHEME);
             } catch (IOException ex) {
@@ -359,36 +380,23 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    private abstract static class CheckIsConnAdapter extends RBuiltinNode {
-        protected RConnection checkIsConnection(Object con) throws RError {
-            if (!(con instanceof RConnection)) {
-                throw RError.error(this, RError.Message.NOT_CONNECTION, "con");
-            } else {
-                return (RConnection) con;
-            }
-        }
-    }
-
-    @RBuiltin(name = "summary.connection", kind = INTERNAL, parameterNames = {"object}"}, behavior = IO)
-    public abstract static class Summary extends CheckIsConnAdapter {
+    @RBuiltin(name = "summary.connection", kind = INTERNAL, parameterNames = {"object"}, behavior = IO)
+    public abstract static class Summary extends RBuiltinNode {
         private static final RStringVector NAMES = RDataFactory.createStringVector(new String[]{"description", "class", "mode", "text", "opened", "can read", "can write"},
                         RDataFactory.COMPLETE_VECTOR);
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("object").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
+        }
+
         @Specialization
         @TruffleBoundary
-        protected RList summary(Object object) {
-            BaseRConnection baseCon;
-            if (object instanceof Integer) {
-                baseCon = RContext.getInstance().stateRConnection.getConnection((int) object);
-            } else if (object instanceof Double) {
-                baseCon = RContext.getInstance().stateRConnection.getConnection((int) Math.floor((Double) object));
-            } else {
-                RConnection con = checkIsConnection(object);
-                baseCon = getBaseConnection(con);
-            }
+        protected RList summary(int object) {
+            BaseRConnection baseCon = RConnection.fromIndex(object);
             Object[] data = new Object[NAMES.getLength()];
             data[0] = baseCon.getSummaryDescription();
-            data[1] = baseCon.getClassHierarchy().getDataAt(0);
+            data[1] = baseCon.getConnectionClass().getPrintName();
             data[2] = baseCon.getOpenMode().summaryString();
             data[3] = baseCon.getSummaryText();
             data[4] = baseCon.isOpen() ? "opened" : "closed";
@@ -410,9 +418,9 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object open(RConnection con, String open, @SuppressWarnings("unused") boolean blocking) {
+        protected Object open(int con, String open, @SuppressWarnings("unused") boolean blocking) {
             try {
-                BaseRConnection baseConn = getBaseConnection(con);
+                BaseRConnection baseConn = getBaseConnection(RConnection.fromIndex(con));
                 if (baseConn.isClosed()) {
                     throw RError.error(this, RError.Message.INVALID_CONNECTION);
                 }
@@ -439,8 +447,8 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RLogicalVector isOpen(RConnection con, int rw) {
-            BaseRConnection baseCon = getBaseConnection(con);
+        protected RLogicalVector isOpen(int con, int rw) {
+            BaseRConnection baseCon = getBaseConnection(RConnection.fromIndex(con));
             boolean result = baseCon.isOpen();
             switch (rw) {
                 case 0:
@@ -469,34 +477,19 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object close(RConnection con, @SuppressWarnings("unused") String type) {
+        protected Object close(int con, @SuppressWarnings("unused") String type) {
+            BaseRConnection connection = RConnection.fromIndex(con);
             try {
-                con.closeAndDestroy();
+                connection.closeAndDestroy();
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
             }
             return RNull.instance;
         }
-
-    }
-
-    /**
-     * This is inherited by the {@code readXX/writeXXX} builtins that are required to "close" a
-     * connection that they opened. It does not destroy the connection.
-     */
-    private abstract static class InternalCloseHelper extends RBuiltinNode {
-        protected void internalClose(RConnection con) throws RError {
-            try {
-                BaseRConnection baseConn = getBaseConnection(con);
-                baseConn.close();
-            } catch (IOException ex) {
-                throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
-            }
-        }
     }
 
     @RBuiltin(name = "readLines", kind = INTERNAL, parameterNames = {"con", "n", "ok", "warn", "encoding", "skipNul"}, behavior = IO)
-    public abstract static class ReadLines extends InternalCloseHelper {
+    public abstract static class ReadLines extends RBuiltinNode {
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -510,9 +503,9 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object readLines(RConnection con, int n, boolean ok, boolean warn, @SuppressWarnings("unused") String encoding, boolean skipNul) {
+        protected Object readLines(int con, int n, boolean ok, boolean warn, @SuppressWarnings("unused") String encoding, boolean skipNul) {
             // TODO implement all the arguments
-            try (RConnection openConn = con.forceOpen("rt")) {
+            try (RConnection openConn = RConnection.fromIndex(con).forceOpen("rt")) {
                 String[] lines = openConn.readLines(n, warn, skipNul);
                 if (n > 0 && lines.length < n && !ok) {
                     throw RError.error(this, RError.Message.TOO_FEW_LINES_READ_LINES);
@@ -526,7 +519,7 @@ public abstract class ConnectionFunctions {
     }
 
     @RBuiltin(name = "writeLines", visibility = OFF, kind = INTERNAL, parameterNames = {"text", "con", "sep", "useBytes"}, behavior = IO)
-    public abstract static class WriteLines extends InternalCloseHelper {
+    public abstract static class WriteLines extends RBuiltinNode {
         @Override
         protected void createCasts(CastBuilder casts) {
             casts.arg("text").asStringVector().mustBe(instanceOf(RAbstractStringVector.class));
@@ -537,8 +530,8 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RNull writeLines(RAbstractStringVector text, RConnection con, String sep, boolean useBytes) {
-            try (RConnection openConn = con.forceOpen("wt")) {
+        protected RNull writeLines(RAbstractStringVector text, int con, String sep, boolean useBytes) {
+            try (RConnection openConn = RConnection.fromIndex(con).forceOpen("wt")) {
                 openConn.writeLines(text, sep, useBytes);
             } catch (IOException x) {
                 throw RError.error(this, RError.Message.ERROR_WRITING_CONNECTION, x.getMessage());
@@ -557,9 +550,9 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RNull flush(RConnection con) {
+        protected RNull flush(int con) {
             try {
-                con.flush();
+                RConnection.fromIndex(con).flush();
             } catch (IOException x) {
                 throw RError.error(this, RError.Message.ERROR_FLUSHING_CONNECTION, x.getMessage());
             }
@@ -580,8 +573,8 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object pushBack(RAbstractStringVector data, RConnection connection, boolean newLine, @SuppressWarnings("unused") int type) {
-            connection.pushBack(data, newLine);
+        protected Object pushBack(RAbstractStringVector data, int connection, boolean newLine, @SuppressWarnings("unused") int type) {
+            RConnection.fromIndex(connection).pushBack(data, newLine);
             return RNull.instance;
         }
 
@@ -595,8 +588,8 @@ public abstract class ConnectionFunctions {
         }
 
         @Specialization
-        protected int pushBackLength(RConnection connection) {
-            return connection.pushBackLength();
+        protected int pushBackLength(int connection) {
+            return RConnection.fromIndex(connection).pushBackLength();
         }
 
     }
@@ -610,15 +603,15 @@ public abstract class ConnectionFunctions {
         }
 
         @Specialization
-        protected RNull pushBackClear(RConnection connection) {
-            connection.pushBackClear();
+        protected RNull pushBackClear(int connection) {
+            RConnection.fromIndex(connection).pushBackClear();
             return RNull.instance;
         }
 
     }
 
     @RBuiltin(name = "readChar", kind = INTERNAL, parameterNames = {"con", "nchars", "useBytes"}, behavior = IO)
-    public abstract static class ReadChar extends InternalCloseHelper {
+    public abstract static class ReadChar extends RBuiltinNode {
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -629,14 +622,14 @@ public abstract class ConnectionFunctions {
 
         @SuppressWarnings("unused")
         @Specialization(guards = "ncharsEmpty(nchars)")
-        protected RStringVector readCharNcharsEmpty(RConnection con, RAbstractIntVector nchars, boolean useBytes) {
+        protected RStringVector readCharNcharsEmpty(int con, RAbstractIntVector nchars, boolean useBytes) {
             return RDataFactory.createEmptyStringVector();
         }
 
         @Specialization(guards = "!ncharsEmpty(nchars)")
         @TruffleBoundary
-        protected RStringVector readChar(RConnection con, RAbstractIntVector nchars, boolean useBytes) {
-            try (RConnection openConn = con.forceOpen("rb")) {
+        protected RStringVector readChar(int con, RAbstractIntVector nchars, boolean useBytes) {
+            try (RConnection openConn = RConnection.fromIndex(con).forceOpen("rb")) {
                 String[] data = new String[nchars.getLength()];
                 for (int i = 0; i < data.length; i++) {
                     data[i] = openConn.readChar(nchars.getDataAt(i), useBytes);
@@ -654,11 +647,12 @@ public abstract class ConnectionFunctions {
     }
 
     @RBuiltin(name = "writeChar", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "con", "nchars", "sep", "useBytes"}, behavior = IO)
-    public abstract static class WriteChar extends InternalCloseHelper {
+    public abstract static class WriteChar extends RBuiltinNode {
         @Override
         protected void createCasts(CastBuilder casts) {
             casts.arg("object").asStringVector();
-            casts.arg("con").mustBe(instanceOf(RConnection.class).or(instanceOf(RAbstractRawVector.class)));
+            casts.arg("con").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().mustBe(integerValue().or(rawValue())).mapIf(integerValue(),
+                            asIntegerVector().setNext(findFirst().integerElement()));
             Casts.nchars(casts);
             casts.arg("sep").allowNull().mustBe(stringValue());
             Casts.useBytes(casts);
@@ -666,8 +660,8 @@ public abstract class ConnectionFunctions {
 
         @TruffleBoundary
         @Specialization
-        protected RNull writeChar(RAbstractStringVector object, RConnection con, RAbstractIntVector nchars, RAbstractStringVector sep, boolean useBytes) {
-            try (RConnection openConn = con.forceOpen("wb")) {
+        protected RNull writeChar(RAbstractStringVector object, int con, RAbstractIntVector nchars, RAbstractStringVector sep, boolean useBytes) {
+            try (RConnection openConn = RConnection.fromIndex(con).forceOpen("wb")) {
                 int length = object.getLength();
                 for (int i = 0; i < length; i++) {
                     String s = object.getDataAt(i);
@@ -687,7 +681,7 @@ public abstract class ConnectionFunctions {
         @SuppressWarnings("unused")
         @TruffleBoundary
         @Specialization
-        protected RNull writeChar(RAbstractStringVector object, RConnection con, RAbstractIntVector nchars, RNull sep, boolean useBytes) {
+        protected RNull writeChar(RAbstractStringVector object, int con, RAbstractIntVector nchars, RNull sep, boolean useBytes) {
             throw RError.nyi(this, "writeChar(sep=NULL)");
         }
     }
@@ -706,63 +700,49 @@ public abstract class ConnectionFunctions {
         return buffer.order(nb);
     }
 
-    private abstract static class BinRBuiltinNode extends InternalCloseHelper {
-        protected void n(CastBuilder casts) {
-            casts.arg("n").asIntegerVector().findFirst().mustBe(gte(0));
-        }
-
-        protected void size(CastBuilder casts) {
-            casts.arg("size").asIntegerVector().findFirst();
-        }
-
-        protected void swap(CastBuilder casts) {
-            casts.arg("swap").asLogicalVector().findFirst().notNA().map(toBoolean());
-        }
-    }
-
     @RBuiltin(name = "readBin", kind = INTERNAL, parameterNames = {"con", "what", "n", "size", "signed", "swap"}, behavior = IO)
-    public abstract static class ReadBin extends BinRBuiltinNode {
+    public abstract static class ReadBin extends RBuiltinNode {
 
         @Override
         protected void createCasts(CastBuilder casts) {
             // TODO con can be a RAWSXP (not implemented)
             Casts.connection(casts);
             casts.arg("what").asStringVector().findFirst();
-            n(casts);
-            size(casts);
+            Casts.n(casts);
+            Casts.size(casts);
             casts.arg("signed").asLogicalVector().findFirst().notNA().map(toBoolean());
-            swap(casts);
+            Casts.swap(casts);
         }
 
         @Specialization
         @TruffleBoundary
-        protected Object readBin(RConnection con, String what, int n, @SuppressWarnings("unused") int size, @SuppressWarnings("unused") boolean signed,
-                        boolean swap) {
+        protected Object readBin(int con, String what, int n, @SuppressWarnings("unused") int size, @SuppressWarnings("unused") boolean signed, boolean swap) {
             RVector<?> result = null;
-            try (RConnection openConn = con.forceOpen("rb")) {
+            BaseRConnection connection = RConnection.fromIndex(con);
+            try (RConnection openConn = connection.forceOpen("rb")) {
                 if (getBaseConnection(openConn).getOpenMode().isText()) {
                     throw RError.error(this, RError.Message.ONLY_READ_BINARY_CONNECTION);
                 }
                 switch (what) {
                     case "int":
                     case "integer":
-                        result = readInteger(con, n, swap);
+                        result = readInteger(connection, n, swap);
                         break;
                     case "double":
                     case "numeric":
-                        result = readDouble(con, n, swap);
+                        result = readDouble(connection, n, swap);
                         break;
                     case "complex":
-                        result = readComplex(con, n, swap);
+                        result = readComplex(connection, n, swap);
                         break;
                     case "character":
-                        result = readString(con, n);
+                        result = readString(connection, n);
                         break;
                     case "logical":
-                        result = readLogical(con, n, swap);
+                        result = readLogical(connection, n, swap);
                         break;
                     case "raw":
-                        result = readRaw(con, n);
+                        result = readRaw(connection, n);
                         break;
                     default:
                         throw RInternalError.shouldNotReachHere();
@@ -992,33 +972,34 @@ public abstract class ConnectionFunctions {
     }
 
     @RBuiltin(name = "writeBin", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "con", "size", "swap", "useBytes"}, behavior = IO)
-    public abstract static class WriteBin extends BinRBuiltinNode {
+    public abstract static class WriteBin extends RBuiltinNode {
 
         @Override
         protected void createCasts(CastBuilder casts) {
             // TODO atomic, i.e. not RList or RExpression
             casts.arg("object").asVector().mustBe(instanceOf(RAbstractVector.class));
-            Casts.connection(casts);
-            size(casts);
-            swap(casts);
+            casts.arg("con").defaultError(Message.INVALID_CONNECTION).mustBe(integerValue().or(rawValue())).mapIf(integerValue(), asIntegerVector().setNext(findFirst().integerElement()));
+            Casts.size(casts);
+            Casts.swap(casts);
             Casts.useBytes(casts);
         }
 
         @TruffleBoundary
         @Specialization
-        protected Object writeBin(RAbstractVector object, RConnection con, int size, boolean swap, boolean useBytes, //
+        protected Object writeBin(RAbstractVector object, int con, int size, boolean swap, boolean useBytes,
                         @Cached("create()") WriteDataNode writeData) {
             if (object instanceof RList || object instanceof RExpression) {
                 throw RError.error(this, RError.Message.INVALID_ARGUMENT, "object");
             }
             if (object.getLength() > 0) {
-                try (RConnection openConn = con.forceOpen("wb")) {
+                RConnection connection = RConnection.fromIndex(con);
+                try (RConnection openConn = connection.forceOpen("wb")) {
                     if (getBaseConnection(openConn).isTextMode()) {
                         throw RError.error(this, RError.Message.ONLY_WRITE_BINARY_CONNECTION);
                     }
                     ByteBuffer buffer = writeData.execute(object, size, swap, useBytes);
                     buffer.flip();
-                    con.writeBin(buffer);
+                    connection.writeBin(buffer);
                 } catch (IOException x) {
                     throw RError.error(this, RError.Message.ERROR_WRITING_CONNECTION, x.getMessage());
                 }
@@ -1027,7 +1008,7 @@ public abstract class ConnectionFunctions {
         }
 
         @Specialization
-        protected RRawVector writeBin(RAbstractVector object, @SuppressWarnings("unused") RAbstractRawVector con, int size, byte swapArg, byte useBytesArg, //
+        protected RRawVector writeBin(RAbstractVector object, @SuppressWarnings("unused") RAbstractRawVector con, int size, byte swapArg, byte useBytesArg,
                         @Cached("create()") WriteDataNode writeData) {
             boolean swap = RRuntime.fromLogical(swapArg);
             boolean useBytes = RRuntime.fromLogical(useBytesArg);
@@ -1047,12 +1028,12 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RConnection getConnection(int what) {
-            BaseRConnection con = RContext.getInstance().stateRConnection.getConnection(what);
+        protected RAbstractIntVector getConnection(int what) {
+            BaseRConnection con = RContext.getInstance().stateRConnection.getConnection(what, false);
             if (con == null) {
                 throw RError.error(this, RError.Message.NO_SUCH_CONNECTION, what);
             } else {
-                return con;
+                return con.asVector();
             }
         }
     }
@@ -1061,7 +1042,7 @@ public abstract class ConnectionFunctions {
     public abstract static class GetAllConnections extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
-        protected RIntVector getAllConnections() {
+        protected RAbstractIntVector getAllConnections() {
             return RContext.getInstance().stateRConnection.getAllConnections();
         }
     }
@@ -1071,13 +1052,12 @@ public abstract class ConnectionFunctions {
         @Override
         protected void createCasts(CastBuilder casts) {
             Casts.connection(casts);
-
         }
 
         @Specialization
         @TruffleBoundary
-        protected byte isSeekable(RConnection con) {
-            return RRuntime.asLogical(con.isSeekable());
+        protected byte isSeekable(int con) {
+            return RRuntime.asLogical(RConnection.fromIndex(con).isSeekable());
         }
     }
 
@@ -1093,10 +1073,10 @@ public abstract class ConnectionFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected long seek(RConnection con, double where, int origin, int rw) {
+        protected long seek(int con, double where, int origin, int rw) {
             long offset = (long) where;
             try {
-                return con.seek(offset, RConnection.SeekMode.values()[origin], RConnection.SeekRWMode.values()[rw]);
+                return RConnection.fromIndex(con).seek(offset, RConnection.SeekMode.values()[origin], RConnection.SeekRWMode.values()[rw]);
             } catch (IOException x) {
                 throw RError.error(this, RError.Message.GENERIC, x.getMessage());
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
index 88021a84b5f6adb5ce46ffaab13c5e7396ca1b25..f3621cd18b11f21d9f71fcad1e2884c95381103d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 
@@ -45,15 +46,15 @@ public abstract class DPut extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("file").mustBe(RConnection.class);
+        casts.arg("file").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
         casts.arg("opts").asIntegerVector().findFirst();
     }
 
     @Specialization
     @TruffleBoundary
-    protected Object dput(Object x, RConnection file, int opts) {
+    protected Object dput(Object x, int file, int opts) {
         String string = RDeparse.deparse(x, RDeparse.DEFAULT_Cutoff, true, opts, -1);
-        try (RConnection openConn = file.forceOpen("wt")) {
+        try (RConnection openConn = RConnection.fromIndex(file).forceOpen("wt")) {
             openConn.writeString(string, true);
         } catch (IOException ex) {
             throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
index df2b9f06e5503c91ba453d557b8443da7b7ac0fc..63b11a187c3b94bd87bc60a62d6323612990d50f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
@@ -38,7 +38,6 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
@@ -344,12 +343,6 @@ public abstract class Identical extends RBuiltinNode {
         return identicalAttr(x, y, numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
     }
 
-    @SuppressWarnings("unused")
-    @Specialization(guards = "argConnections(x, y)")
-    protected byte doInternalIdenticalConnections(Object x, Object y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
-        return RRuntime.asLogical(((RConnection) x).getDescriptor() == ((RConnection) y).getDescriptor());
-    }
-
     @SuppressWarnings("unused")
     @Fallback
     protected byte doInternalIdenticalWrongTypes(Object x, Object y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
@@ -364,10 +357,6 @@ public abstract class Identical extends RBuiltinNode {
         return x instanceof RListBase && y instanceof RListBase;
     }
 
-    protected boolean argConnections(Object x, Object y) {
-        return x instanceof RConnection && y instanceof RConnection;
-    }
-
     public static Identical create() {
         return IdenticalNodeGen.create();
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InheritsBuiltin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InheritsBuiltin.java
index e6c8ec77d4dadf8564670ecbd855c7143a1cf207..d4c874044f992a6f1cd85801353fb18491f6cab5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InheritsBuiltin.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InheritsBuiltin.java
@@ -42,6 +42,6 @@ public abstract class InheritsBuiltin extends RBuiltinNode {
 
     @Specialization
     protected Object doesInherit(Object x, RAbstractStringVector what, boolean which) {
-        return inheritsNode.executeObject(x, what, which);
+        return inheritsNode.execute(x, what, which);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java
index 5d1c2807f96c59cd41141200ce604371605d4f3b..2af41c1a6793d4acef00af3f9e468231748e6f5e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java
@@ -25,23 +25,44 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections.StdConnection;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
 @RBuiltin(name = "isatty", kind = PRIMITIVE, parameterNames = {"con"}, behavior = PURE)
 public abstract class IsATTY extends RBuiltinNode {
 
     @Specialization
-    protected byte isATTY(RConnection con) {
-        return RRuntime.asLogical(con instanceof StdConnection);
+    @TruffleBoundary
+    protected byte isATTYNonConnection(RAbstractIntVector con) {
+        if (con.getLength() == 1) {
+            RStringVector clazz = con.getClassHierarchy();
+            for (int i = 0; i < clazz.getLength(); i++) {
+                if ("connection".equals(clazz.getDataAt(i))) {
+                    RConnection connection = RContext.getInstance().stateRConnection.getConnection(con.getDataAt(0), false);
+                    if (connection != null) {
+                        return RRuntime.asLogical(connection instanceof StdConnection);
+                    } else {
+                        return RRuntime.LOGICAL_FALSE;
+                    }
+                }
+            }
+        }
+        return RRuntime.LOGICAL_FALSE;
     }
 
-    @Specialization(guards = "!isRConnection(con)")
+    @Fallback
+    @TruffleBoundary
     protected byte isATTYNonConnection(@SuppressWarnings("unused") Object con) {
+
         return RRuntime.LOGICAL_FALSE;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java
index 1667cb57214e008f159f6c76bb50e95fb9a146a0..69fdb3023654692033d9d9da2ecfeab3e84eb0ca 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -56,14 +57,15 @@ public class LoadSaveFunctions {
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.arg("con").mustBe(instanceOf(RConnection.class));
+            casts.arg("con").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
             casts.arg("envir").mustNotBeNull(RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class));
             casts.arg("verbose").asLogicalVector().findFirst().map(toBoolean());
         }
 
         @Specialization
         @TruffleBoundary
-        protected RStringVector load(RConnection con, REnvironment envir, @SuppressWarnings("unused") boolean verbose) {
+        protected RStringVector load(int conIndex, REnvironment envir, @SuppressWarnings("unused") boolean verbose) {
+            RConnection con = RConnection.fromIndex(conIndex);
             try (RConnection openConn = con.forceOpen("r")) {
                 String s = openConn.readChar(5, true);
                 if (s.equals("RDA2\n") || s.equals("RDB2\n") || s.equals("RDX2\n")) {
@@ -191,7 +193,7 @@ public class LoadSaveFunctions {
         @Override
         protected void createCasts(CastBuilder casts) {
             casts.arg("list").mustBe(stringValue()).asStringVector().mustBe(notEmpty(), RError.Message.FIRST_ARGUMENT_NOT_CHARVEC).findFirst();
-            casts.arg("con").mustBe(instanceOf(RConnection.class));
+            ConnectionFunctions.Casts.connection(casts);
             casts.arg("ascii").mustBe(logicalValue(), RError.Message.ASCII_NOT_LOGICAL);
             casts.arg("version").allowNull().mustBe(integerValue());
             casts.arg("environment").mustNotBeNull(RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class));
@@ -199,8 +201,7 @@ public class LoadSaveFunctions {
         }
 
         @Specialization
-        protected Object saveToConn(VirtualFrame frame, RAbstractStringVector list, RConnection conn, byte asciiLogical, @SuppressWarnings("unused") RNull version, REnvironment envir,
-                        boolean evalPromises, //
+        protected Object saveToConn(VirtualFrame frame, RAbstractStringVector list, int con, byte asciiLogical, @SuppressWarnings("unused") RNull version, REnvironment envir, boolean evalPromises,
                         @Cached("new()") PromiseCheckHelperNode promiseHelper) {
             RPairList prev = null;
             Object toSave = RNull.instance;
@@ -223,7 +224,7 @@ public class LoadSaveFunctions {
                 prev = pl;
             }
             boolean ascii = RRuntime.fromLogical(asciiLogical);
-            doSaveConn(toSave, conn, ascii);
+            doSaveConn(toSave, RConnection.fromIndex(con), ascii);
             return RNull.instance;
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index 92532cc0dc3eca48502b09a5dea2b6d8a3b33162..69c479a93f36952fce3275cc57265b45a24cb5cc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -99,7 +99,7 @@ public abstract class Parse extends RBuiltinNode {
     @Override
     protected void createCasts(CastBuilder casts) {
         // Note: string is captured by the R wrapper and transformed to a file, other types not
-        casts.arg("conn").mustBe(RConnection.class, MUST_BE_STRING_OR_CONNECTION, "file");
+        casts.arg("conn").defaultError(MUST_BE_STRING_OR_CONNECTION, "file").mustNotBeNull().asIntegerVector().findFirst();
         casts.arg("n").asIntegerVector().findFirst(RRuntime.INT_NA).notNA(-1);
         casts.arg("text").allowNull().asStringVector();
         casts.arg("prompt").asStringVector().findFirst("?");
@@ -108,27 +108,28 @@ public abstract class Parse extends RBuiltinNode {
 
     @TruffleBoundary
     @Specialization
-    protected Object parse(RConnection conn, int n, @SuppressWarnings("unused") RNull text, String prompt, Object srcFile, String encoding) {
+    protected Object parse(int conn, int n, @SuppressWarnings("unused") RNull text, String prompt, Object srcFile, String encoding) {
         String[] lines;
-        if (conn == StdConnections.getStdin()) {
+        RConnection connection = RConnection.fromIndex(conn);
+        if (connection == StdConnections.getStdin()) {
             throw RError.nyi(this, "parse from stdin not implemented");
         }
-        try (RConnection openConn = conn.forceOpen("r")) {
+        try (RConnection openConn = connection.forceOpen("r")) {
             lines = openConn.readLines(0, false, false);
         } catch (IOException ex) {
             throw RError.error(this, RError.Message.PARSE_ERROR);
         }
-        return doParse(conn, n, lines, prompt, srcFile, encoding);
+        return doParse(connection, n, lines, prompt, srcFile, encoding);
     }
 
     @TruffleBoundary
     @Specialization
-    protected Object parse(RConnection conn, int n, RAbstractStringVector text, String prompt, Object srcFile, String encoding) {
-        return doParse(conn, n, text.materialize().getDataWithoutCopying(), prompt, srcFile, encoding);
+    protected Object parse(int conn, int n, RAbstractStringVector text, String prompt, Object srcFile, String encoding) {
+        RConnection connection = RConnection.fromIndex(conn);
+        return doParse(connection, n, text.materialize().getDataWithoutCopying(), prompt, srcFile, encoding);
     }
 
-    @SuppressWarnings("unused")
-    private Object doParse(RConnection conn, int n, String[] lines, String prompt, Object srcFile, String encoding) {
+    private Object doParse(RConnection conn, int n, String[] lines, @SuppressWarnings("unused") String prompt, Object srcFile, @SuppressWarnings("unused") String encoding) {
         String coalescedLines = coalesce(lines);
         if (coalescedLines.length() == 0 || n == 0) {
             return RDataFactory.createExpression(new Object[0]);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java
index 2d9f66c7dec0eed51f9b65ea35d070586a56ca93..f8540255637ae2dd31f28f3b1d15270424db9c95 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java
@@ -40,6 +40,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.DCF;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -53,16 +54,16 @@ public abstract class ReadDCF extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("conn").mustBe(RConnection.class);
+        casts.arg("conn").defaultError(Message.INVALID_CONNECTION).asIntegerVector().findFirst();
         casts.arg("fields").mapNull(emptyStringVector()).asStringVector();
         casts.arg("keepwhite").mapNull(emptyStringVector()).asStringVector();
     }
 
     @Specialization
     @TruffleBoundary
-    protected RStringVector doReadDCF(RConnection conn, RAbstractStringVector fields, RAbstractStringVector keepWhite) {
+    protected RStringVector doReadDCF(int conn, RAbstractStringVector fields, RAbstractStringVector keepWhite) {
         DCF dcf = null;
-        try (RConnection openConn = conn.forceOpen("r")) {
+        try (RConnection openConn = RConnection.fromIndex(conn).forceOpen("r")) {
             Set<String> keepWhiteSet = null;
             if (keepWhite.getLength() > 0) {
                 keepWhiteSet = new HashSet<>(keepWhite.getLength());
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
index 28ed2c67637d61df73ae84d1d09a32aa9b3d1c45..6a4d2b0e3a599bbb0089d72d2dd6ae6b81277597 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
@@ -38,6 +38,7 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
@@ -102,7 +103,7 @@ public abstract class Scan extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("file").mustBe(RConnection.class);
+        casts.arg("file").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
 
         casts.arg("what").asVector();
 
@@ -145,7 +146,7 @@ public abstract class Scan extends RBuiltinNode {
 
     @Specialization
     @TruffleBoundary
-    protected Object doScan(RConnection file, RAbstractVector what, int nmax, String sep, String dec, String quotes, int nskip,
+    protected Object doScan(int file, RAbstractVector what, int nmax, String sep, String dec, String quotes, int nskip,
                     int nlines, RAbstractStringVector naStringsVec, boolean flush, boolean fill, RAbstractLogicalVector stripVec,
                     boolean quiet, boolean blSkip, boolean multiLine, int commentChar, boolean escapes,
                     String encoding, boolean skipNull) {
@@ -185,7 +186,7 @@ public abstract class Scan extends RBuiltinNode {
         data.skipNull = skipNull;
 
         // TODO: quite a few more things happen in GNU R around connections
-        data.con = file;
+        data.con = RConnection.fromIndex(file);
 
         data.save = 0;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java
index c82ccf48af8b739cac5ab953c713476211f24c9d..5ae7ffb89f7a1ab8b17d4e5daa0635965c4c9112 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java
@@ -22,7 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asIntegerVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.findFirst;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.rawValue;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
@@ -34,6 +38,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -47,13 +52,12 @@ public class SerializeFunctions {
 
     public abstract static class Adapter extends RBuiltinNode {
         @TruffleBoundary
-        protected Object doUnserializeFromConnBase(RConnection conn, @SuppressWarnings("unused") REnvironment refhook) {
-            try (RConnection openConn = conn.forceOpen("rb")) {
+        protected Object doUnserializeFromConnBase(int connIndex, @SuppressWarnings("unused") REnvironment refhook) {
+            try (RConnection openConn = RConnection.fromIndex(connIndex).forceOpen("rb")) {
                 if (!openConn.canRead()) {
                     throw RError.error(this, RError.Message.CONNECTION_NOT_OPEN_READ);
                 }
-                Object result = RSerialize.unserialize(openConn);
-                return result;
+                return RSerialize.unserialize(openConn);
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
             }
@@ -65,10 +69,10 @@ public class SerializeFunctions {
         }
 
         @TruffleBoundary
-        protected Object doSerializeToConnBase(Object object, RConnection conn, int type, @SuppressWarnings("unused") byte xdrLogical, @SuppressWarnings("unused") RNull version,
+        protected Object doSerializeToConnBase(Object object, int connIndex, int type, @SuppressWarnings("unused") byte xdrLogical, @SuppressWarnings("unused") RNull version,
                         @SuppressWarnings("unused") RNull refhook) {
             // xdr is only relevant if ascii is false
-            try (RConnection openConn = conn.forceOpen(type != RSerialize.XDR ? "wt" : "wb")) {
+            try (RConnection openConn = RConnection.fromIndex(connIndex).forceOpen(type != RSerialize.XDR ? "wt" : "wb")) {
                 if (!openConn.canWrite()) {
                     throw RError.error(this, RError.Message.CONNECTION_NOT_OPEN_WRITE);
                 }
@@ -83,7 +87,7 @@ public class SerializeFunctions {
         }
 
         protected void connection(CastBuilder casts) {
-            casts.arg("con").mustBe(instanceOf(RConnection.class));
+            casts.arg("con").mustNotBeNull().mustBe(integerValue()).asIntegerVector().findFirst();
         }
     }
 
@@ -95,12 +99,12 @@ public class SerializeFunctions {
         }
 
         @Specialization
-        protected Object doUnserializeFromConn(RConnection conn, @SuppressWarnings("unused") RNull refhook) {
+        protected Object doUnserializeFromConn(int conn, @SuppressWarnings("unused") RNull refhook) {
             return doUnserializeFromConnBase(conn, null);
         }
 
         @Specialization
-        protected Object doUnserializeFromConn(RConnection conn, @SuppressWarnings("unused") REnvironment refhook) {
+        protected Object doUnserializeFromConn(int conn, @SuppressWarnings("unused") REnvironment refhook) {
             // TODO figure out what this really means?
             return doUnserializeFromConnBase(conn, null);
         }
@@ -116,7 +120,7 @@ public class SerializeFunctions {
         }
 
         @Specialization
-        protected Object doSerializeToConn(Object object, RConnection conn, byte asciiLogical, RNull version, RNull refhook) {
+        protected Object doSerializeToConn(Object object, int conn, byte asciiLogical, RNull version, RNull refhook) {
             int type;
             if (asciiLogical == RRuntime.LOGICAL_NA) {
                 type = RSerialize.ASCII_HEX;
@@ -133,11 +137,12 @@ public class SerializeFunctions {
     public abstract static class Unserialize extends Adapter {
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.arg("con").mustBe(instanceOf(RConnection.class).or(instanceOf(RAbstractRawVector.class)));
+            casts.arg("con").defaultError(Message.INVALID_CONNECTION).mustNotBeNull().mustBe(integerValue().or(rawValue())).mapIf(integerValue(),
+                            asIntegerVector().setNext(findFirst().integerElement()));
         }
 
         @Specialization
-        protected Object unSerialize(RConnection conn, @SuppressWarnings("unused") RNull refhook) {
+        protected Object unSerialize(int conn, @SuppressWarnings("unused") RNull refhook) {
             return doUnserializeFromConnBase(conn, null);
         }
 
@@ -151,12 +156,12 @@ public class SerializeFunctions {
     public abstract static class Serialize extends Adapter {
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.arg("con").allowNull().mustBe(instanceOf(RConnection.class));
+            casts.arg("con").allowNull().mustBe(integerValue()).asIntegerVector().findFirst();
             casts.arg("type").asIntegerVector().findFirst();
         }
 
         @Specialization
-        protected Object serialize(Object object, RConnection conn, int type, RNull version, RNull refhook) {
+        protected Object serialize(Object object, int conn, int type, RNull version, RNull refhook) {
             return doSerializeToConnBase(object, conn, type, RRuntime.LOGICAL_NA, version, refhook);
         }
 
@@ -178,7 +183,7 @@ public class SerializeFunctions {
         }
 
         @Specialization
-        protected Object serializeB(Object object, RConnection conn, byte xdrLogical, RNull version, RNull refhook) {
+        protected Object serializeB(Object object, int conn, byte xdrLogical, RNull version, RNull refhook) {
             if (!RRuntime.fromLogical(xdrLogical)) {
                 throw RError.nyi(this, "xdr==FALSE");
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java
index 63bae90dc6e0cda03887b0cd17e2a47432be4641..b688de5f2f3f3423e9c5f3f28ba5e49ddc30b1fc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java
@@ -22,9 +22,7 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
@@ -34,6 +32,7 @@ import java.io.IOException;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -46,7 +45,7 @@ public class SinkFunctions {
     public abstract static class Sink extends RBuiltinNode {
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.arg("file").mustBe(instanceOf(RConnection.class).or(integerValue()));
+            casts.arg("file").mustNotBeNull().mustBe(Predef.integerValue()).asIntegerVector().findFirst();
             casts.arg("closeOnExit").asLogicalVector().findFirst().notNA().map(toBoolean());
             casts.arg("type").asLogicalVector().findFirst().notNA().map(toBoolean());
             casts.arg("split").asLogicalVector().findFirst().notNA().map(toBoolean());
@@ -54,31 +53,27 @@ public class SinkFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RNull sink(RConnection conn, boolean closeOnExit, boolean errcon, boolean split) {
+        protected RNull sink(int file, boolean closeOnExit, boolean errcon, boolean split) {
             if (split) {
                 // TODO
                 throw RError.nyi(this, "split");
             }
-            if (errcon) {
-                StdConnections.divertErr(conn);
+            if (file == -1) {
+                try {
+                    StdConnections.popDivertOut();
+                } catch (IOException ex) {
+                    throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
+                }
             } else {
-                StdConnections.pushDivertOut(conn, closeOnExit);
+                RConnection conn = RConnection.fromIndex(file);
+                if (errcon) {
+                    StdConnections.divertErr(conn);
+                } else {
+                    StdConnections.pushDivertOut(conn, closeOnExit);
+                }
             }
             return RNull.instance;
         }
-
-        @SuppressWarnings("unused")
-        @Specialization
-        @TruffleBoundary
-        protected RNull sink(int conn, boolean closeOnExit, boolean isMessage, boolean split) {
-            try {
-                StdConnections.popDivertOut();
-            } catch (IOException ex) {
-                throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
-            }
-            return RNull.instance;
-        }
-
     }
 
     @RBuiltin(name = "sink.number", kind = INTERNAL, parameterNames = {"type"}, behavior = IO)
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 24c3e23bd4335004ef30be42e6ecbf572e6e52da..76f8082bdc4eb0bc4ab028f23c6fe318ac9a0d42 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -581,7 +581,7 @@ public class ForeignFunctions {
                 case "countfields":
                     return new CountFields();
                 case "readtablehead":
-                    return new ReadTableHead();
+                    return ReadTableHeadNodeGen.create();
                 case "download":
                     return DownloadNodeGen.create();
                 case "termsform":
@@ -664,7 +664,7 @@ public class ForeignFunctions {
             switch (name) {
                 // tools
                 case "writetable":
-                    return new WriteTable();
+                    return WriteTableNodeGen.create();
                 case "typeconvert":
                     return TypeConvertNodeGen.create();
                 case "C_parseRd":
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java
index 876a634a898fc79131865337c7aa9473141ba39d..e0c3cec0f8410e73c9d344af9bcbf2b0bc366971 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java
@@ -14,22 +14,29 @@ package com.oracle.truffle.r.nodes.builtin.base.foreign;
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-public final class ReadTableHead extends RExternalBuiltinNode {
+public abstract class ReadTableHead extends RExternalBuiltinNode.Arg7 {
 
     @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg(0).defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
+        casts.arg(1).mustNotBeNull().asIntegerVector().findFirst();
+    }
+
+    @Specialization
     @TruffleBoundary
-    public Object call(RArgsValuesAndNames args) {
+    public RAbstractStringVector read(int con, int nlines, @SuppressWarnings("unused") Object commentChar, @SuppressWarnings("unused") Object blankLinesSkip,
+                    @SuppressWarnings("unused") Object quote, @SuppressWarnings("unused") Object sep, @SuppressWarnings("unused") Object skipNull) {
         // TODO This is quite incomplete and just uses readLines, which works for some inputs
-        Object[] argValues = args.getArguments();
-        RConnection conn = (RConnection) argValues[0];
-        int nlines = castInt(castVector(argValues[1]));
-        try (RConnection openConn = conn.forceOpen("r")) {
+        try (RConnection openConn = RConnection.fromIndex(con).forceOpen("r")) {
             return RDataFactory.createStringVector(openConn.readLines(nlines, true, false), RDataFactory.COMPLETE_VECTOR);
         } catch (IOException ex) {
             errorProfile.enter();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java
index 0ecf5afe88050f58b605bd30874151ae7e046d87..ec1805d01241ed3bb8f1f4eca46d9d7d28950803 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java
@@ -11,17 +11,23 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.printer.ComplexVectorPrinter;
 import com.oracle.truffle.r.nodes.builtin.base.printer.DoubleVectorPrinter;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -37,51 +43,94 @@ 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.RAbstractRawVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 //Transcribed from GnuR, library/utils/src/io.c
 
-public final class WriteTable extends RExternalBuiltinNode {
+public abstract class WriteTable extends RExternalBuiltinNode.Arg11 {
 
-    @TruffleBoundary
-    private static Object execute(RConnection con, Object xx, int nr, int nc, Object rnames, String csep, String ceol, String cna, char cdec, boolean qmethod, boolean[] quoteCol, boolean quoteRn)
-                    throws IOException, IllegalArgumentException {
-        String tmp = null;
-        if (RRuntime.hasRClass(xx, RRuntime.CLASS_DATA_FRAME)) {
-            executeDataFrame(con, (RVector<?>) xx, nr, nc, rnames, csep, ceol, cna, cdec, qmethod, quoteCol, quoteRn);
-        } else { /* A matrix */
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        // file
+        casts.arg(1).defaultError(Message.INVALID_CONNECTION).mustNotBeNull().asIntegerVector().findFirst();
+        // nrows
+        casts.arg(2).mustNotBeNull().asIntegerVector().findFirst().notNA();
+        // nc
+        casts.arg(3).mustNotBeNull().asIntegerVector().findFirst().notNA();
+        // rnames
+        casts.arg(4).allowNull().mustBe(stringValue()).asStringVector();
+        // sep
+        casts.arg(5).mustNotBeNull().mustBe(stringValue()).asStringVector().findFirst();
+        // eol
+        casts.arg(6).mustNotBeNull().mustBe(stringValue()).asStringVector().findFirst();
+        // na
+        casts.arg(7).mustNotBeNull().mustBe(stringValue()).asStringVector().findFirst();
+        // dec
+        casts.arg(8).mustNotBeNull().mustBe(stringValue()).asStringVector().findFirst().mustBe(Predef.length(1), RError.Message.GENERIC, "'dec' must be a single character");
+        // quote
+        casts.arg(9).mustNotBeNull().asIntegerVector();
+        // qmethod
+        casts.arg(10).mustNotBeNull().asLogicalVector().findFirst().notNA().map(toBoolean());
+    }
 
-            // if (!isVectorAtomic(x))
-            // UNIMPLEMENTED_TYPE("write.table, matrix method", x);
-            RVector<?> x = (RVector<?>) xx;
-            /* quick integrity check */
-            if (x.getLength() != nr * nc) {
-                throw new IllegalArgumentException("corrupt matrix -- dims not not match length");
+    // Transcribed from GnuR, library/utils/src/io.c
+
+    @Specialization
+    @TruffleBoundary
+    protected static Object writetable(Object xx, int file, int nr, int nc, Object rnames, String csep, String ceol, String cna, String dec, RAbstractIntVector quote, boolean qmethod) {
+        char cdec = dec.charAt(0);
+        boolean[] quoteCol = new boolean[nc];
+        boolean quoteRn = false;
+        for (int i = 0; i < quote.getLength(); i++) {
+            int qi = quote.getDataAt(i);
+            if (qi == 0) {
+                quoteRn = true;
+            }
+            if (qi > 0) {
+                quoteCol[qi - 1] = true;
             }
+        }
+        try (RConnection con = RConnection.fromIndex(file).forceOpen("wt")) {
+            String tmp = null;
+            if (RRuntime.hasRClass(xx, RRuntime.CLASS_DATA_FRAME)) {
+                executeDataFrame(con, (RVector<?>) xx, nr, nc, rnames, csep, ceol, cna, cdec, qmethod, quoteCol, quoteRn);
+            } else { /* A matrix */
 
-            for (int i = 0; i < nr; i++) {
-                if (i % 1000 == 999) {
-                    // R_CheckUserInterrupt();
-                }
-                if (!(rnames instanceof RNull)) {
-                    con.writeString(encodeElement2((RStringVector) rnames, i, quoteRn, qmethod, cdec), false);
-                    con.writeString(csep, false);
+                // if (!isVectorAtomic(x))
+                // UNIMPLEMENTED_TYPE("write.table, matrix method", x);
+                RVector<?> x = (RVector<?>) xx;
+                /* quick integrity check */
+                if (x.getLength() != nr * nc) {
+                    throw new IllegalArgumentException("corrupt matrix -- dims not not match length");
                 }
-                for (int j = 0; j < nc; j++) {
-                    if (j > 0) {
+
+                for (int i = 0; i < nr; i++) {
+                    if (i % 1000 == 999) {
+                        // R_CheckUserInterrupt();
+                    }
+                    if (!(rnames instanceof RNull)) {
+                        con.writeString(encodeElement2((RAbstractStringVector) rnames, i, quoteRn, qmethod, cdec), false);
                         con.writeString(csep, false);
                     }
-                    if (isna(x, i + j * nr)) {
-                        tmp = cna;
-                    } else {
-                        tmp = encodeElement2(x, i + j * nr, quoteCol[j], qmethod, cdec);
-                        /* if(cdec) change_dec(tmp, cdec, TYPEOF(x)); */
+                    for (int j = 0; j < nc; j++) {
+                        if (j > 0) {
+                            con.writeString(csep, false);
+                        }
+                        if (isna(x, i + j * nr)) {
+                            tmp = cna;
+                        } else {
+                            tmp = encodeElement2(x, i + j * nr, quoteCol[j], qmethod, cdec);
+                            /* if(cdec) change_dec(tmp, cdec, TYPEOF(x)); */
+                        }
+                        con.writeString(tmp, false);
                     }
-                    con.writeString(tmp, false);
+                    con.writeString(ceol, false);
                 }
-                con.writeString(ceol, false);
-            }
 
+            }
+        } catch (IOException | IllegalArgumentException ex) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.GENERIC, ex.getMessage());
         }
         return RNull.instance;
     }
@@ -215,8 +264,7 @@ public final class WriteTable extends RExternalBuiltinNode {
         }
     }
 
-    @SuppressWarnings("unused")
-    private static String encodeElement(Object x, int indx, char quote, char dec) {
+    private static String encodeElement(Object x, int indx, @SuppressWarnings("unused") char quote, @SuppressWarnings("unused") char dec) {
         if (x instanceof RAbstractDoubleVector) {
             RAbstractDoubleVector v = (RAbstractDoubleVector) x;
             return DoubleVectorPrinter.encodeReal(v.getDataAt(indx));
@@ -248,88 +296,4 @@ public final class WriteTable extends RExternalBuiltinNode {
         }
         return false;
     }
-
-    private void invalidArgument(String name) throws RError {
-        errorProfile.enter();
-        throw RError.error(this, RError.Message.INVALID_ARGUMENT, name);
-    }
-
-    // Transcribed from GnuR, library/utils/src/io.c
-    @TruffleBoundary
-    @Override
-    public Object call(RArgsValuesAndNames args) {
-        Object[] argValues = args.getArguments();
-        Object conArg = argValues[1];
-        RConnection conn;
-        if (!(conArg instanceof RConnection)) {
-            errorProfile.enter();
-            throw RError.error(this, RError.Message.GENERIC, "'file' is not a connection");
-        } else {
-            conn = (RConnection) conArg;
-        }
-        // TODO check connection writeable
-
-        int nr = castInt(castVector(argValues[2]));
-        int nc = castInt(castVector(argValues[3]));
-        Object rnamesArg = argValues[4];
-        Object sepArg = argValues[5];
-        Object eolArg = argValues[6];
-        Object naArg = argValues[7];
-        Object decArg = argValues[8];
-        Object quoteArg = argValues[9];
-        byte qmethod = castLogical(castVector(argValues[10]));
-
-        String csep;
-        String ceol;
-        String cna;
-        String cdec;
-
-        if (nr == RRuntime.INT_NA) {
-            invalidArgument("nr");
-        }
-        if (nc == RRuntime.INT_NA) {
-            invalidArgument("nc");
-        }
-        if (!(rnamesArg instanceof RNull) && isString(rnamesArg) == null) {
-            invalidArgument("rnames");
-        }
-        if ((csep = isString(sepArg)) == null) {
-            invalidArgument("sep");
-        }
-        if ((ceol = isString(eolArg)) == null) {
-            invalidArgument("eol");
-        }
-        if ((cna = isString(naArg)) == null) {
-            invalidArgument("na");
-        }
-        if ((cdec = isString(decArg)) == null) {
-            invalidArgument("dec");
-        }
-        if (qmethod == RRuntime.LOGICAL_NA) {
-            invalidArgument("qmethod");
-        }
-        if (cdec.length() != 1) {
-            errorProfile.enter();
-            throw RError.error(this, RError.Message.GENERIC, "'dec' must be a single character");
-        }
-        boolean[] quoteCol = new boolean[nc];
-        boolean quoteRn = false;
-        RAbstractIntVector quote = (RAbstractIntVector) castVector(quoteArg);
-        for (int i = 0; i < quote.getLength(); i++) {
-            int qi = quote.getDataAt(i);
-            if (qi == 0) {
-                quoteRn = true;
-            }
-            if (qi > 0) {
-                quoteCol[qi - 1] = true;
-            }
-        }
-        try (RConnection openConn = conn.forceOpen("wt")) {
-            execute(openConn, argValues[0], nr, nc, rnamesArg, csep, ceol, cna, cdec.charAt(0), RRuntime.fromLogical(qmethod), quoteCol, quoteRn);
-        } catch (IOException | IllegalArgumentException ex) {
-            errorProfile.enter();
-            throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
-        }
-        return RNull.instance;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
index 6fa4154b256d255e11c3e5f6675b9c2dfbf39e5f..a074107056c22cf53570a699c3c580f5239e0b4b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
@@ -35,8 +35,6 @@ import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RInteger;
 import com.oracle.truffle.r.runtime.data.RString;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
@@ -125,20 +123,6 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
                         (!isLogicOp(factory) && (value instanceof RAbstractStringVector || value instanceof RAbstractRawVector));
     }
 
-    @Specialization(guards = {"isRConnection(left) || isRConnection(right)"})
-    protected Object doConnection(VirtualFrame frame, Object left, Object right, //
-                    @Cached("createRecursive()") BinaryBooleanNode recursive) {
-        Object recursiveLeft = left;
-        if (recursiveLeft instanceof RConnection) {
-            recursiveLeft = RInteger.valueOf(((RConnection) recursiveLeft).getDescriptor());
-        }
-        Object recursiveRight = right;
-        if (recursiveRight instanceof RConnection) {
-            recursiveRight = RInteger.valueOf(((RConnection) recursiveRight).getDescriptor());
-        }
-        return recursive.execute(frame, recursiveLeft, recursiveRight);
-    }
-
     @Specialization(guards = {"isRSymbol(left) || isRSymbol(right)"})
     protected Object doSymbol(VirtualFrame frame, Object left, Object right, //
                     @Cached("createRecursive()") BinaryBooleanNode recursive) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
index 2b27980e31203978e15b7d3d61e985b3d0ebbca3..d535ea92bc02fa1d2c8c9afeac907fcbb0c97534 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
@@ -240,4 +240,28 @@ public abstract class RExternalBuiltinNode extends RBaseNode {
             return execute(castArg(args, 0), castArg(args, 1), castArg(args, 2), castArg(args, 3), castArg(args, 4), castArg(args, 5), castArg(args, 6), castArg(args, 7), castArg(args, 8));
         }
     }
+
+    public abstract static class Arg10 extends RExternalBuiltinNode {
+
+        public abstract Object execute(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 10);
+            return execute(castArg(args, 0), castArg(args, 1), castArg(args, 2), castArg(args, 3), castArg(args, 4), castArg(args, 5), castArg(args, 6), castArg(args, 7), castArg(args, 8),
+                            castArg(args, 9));
+        }
+    }
+
+    public abstract static class Arg11 extends RExternalBuiltinNode {
+
+        public abstract Object execute(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10, Object arg11);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 11);
+            return execute(castArg(args, 0), castArg(args, 1), castArg(args, 2), castArg(args, 3), castArg(args, 4), castArg(args, 5), castArg(args, 6), castArg(args, 7), castArg(args, 8),
+                            castArg(args, 9), castArg(args, 10));
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index a3916806d3e84cff8128b24c8d34ee2c97364b78..b048eb9a5cb1bd50263f4d4e655e3b612a9510fb 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
@@ -87,6 +87,7 @@ import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
 import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
@@ -930,6 +931,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         public Object execute(VirtualFrame frame, RFunction currentFunction, RArgsValuesAndNames orderedArguments, S3Args s3Args) {
             Object result = builtin.executeBuiltin(frame, castArguments(frame, orderedArguments.getArguments()));
             assert result != null : "builtins cannot return 'null': " + builtinDescriptor.getName();
+            assert !(result instanceof RConnection) : "builtins cannot return connection': " + builtinDescriptor.getName();
             visibility.execute(frame, builtinDescriptor.getVisibility());
             return result;
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java
index 4608a96e37c7a0c982bcc6df1a90d3420bf618ab..de2c9eda806469d95e22a5c8c9962e4cafe612a0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java
@@ -75,6 +75,13 @@ public abstract class ShareObjectNode extends Node {
         return value;
     }
 
+    public static <T> T sharePermanent(T value) {
+        if (value instanceof RShareable) {
+            ((RShareable) value).makeSharedPermanent();
+        }
+        return value;
+    }
+
     public static void unshare(Object value) {
         if (value instanceof RShareable) {
             RShareable shareable = (RShareable) value;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java
index 5826e2c9e8a26fdfca94d84000f09d5f1f30a9d1..5fb4081e6c4237c0cc219269515795ab91d732fa 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/InheritsNode.java
@@ -30,7 +30,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 @TypeSystemReference(RTypes.class)
 public abstract class InheritsNode extends RBaseNode {
 
-    public abstract Object executeObject(Object x, Object what, boolean which);
+    public abstract Object execute(Object x, RAbstractStringVector what, boolean which);
 
     protected ClassHierarchyNode createClassHierarchy() {
         return ClassHierarchyNodeGen.create(true, true);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
index 3a77d377e1efc512c34ba49461afb6616cd1d789..e53c581f1a97c8d6741feb1e4dbb16d038e5a219 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
@@ -34,14 +34,12 @@ import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 /**
@@ -79,14 +77,18 @@ public class ConnectionSupport {
         }
 
         @TruffleBoundary
-        public BaseRConnection getConnection(int index) {
+        public BaseRConnection getConnection(int index, boolean throwError) {
+            BaseRConnection conn = null;
             if (index >= 0 && index <= hwm) {
                 WeakReference<BaseRConnection> ref = allConnections.get(index);
                 if (ref != null) {
-                    return ref.get();
+                    conn = ref.get();
                 }
             }
-            return null;
+            if (conn == null && throwError) {
+                throw RError.error(RError.SHOW_CALLER, Message.INVALID_CONNECTION);
+            }
+            return conn;
         }
 
         private int setStdConnection(int index, BaseRConnection con) {
@@ -100,7 +102,7 @@ public class ConnectionSupport {
             return index;
         }
 
-        public RIntVector getAllConnections() {
+        public RAbstractIntVector getAllConnections() {
             ArrayList<Integer> list = new ArrayList<>();
             for (int i = 0; i <= hwm; i++) {
                 WeakReference<BaseRConnection> ref = allConnections.get(i);
@@ -302,7 +304,7 @@ public class ConnectionSupport {
         }
     }
 
-    enum ConnectionClass {
+    public enum ConnectionClass {
         Terminal("terminal"),
         File("file"),
         GZFile("gzfile"),
@@ -311,11 +313,15 @@ public class ConnectionSupport {
         URL("url"),
         Internal("internal");
 
-        final String printName;
+        private final String printName;
 
         ConnectionClass(String printName) {
             this.printName = printName;
         }
+
+        public String getPrintName() {
+            return printName;
+        }
     }
 
     public static final String FILE_URL_PREFIX = "file://";
@@ -328,22 +334,6 @@ public class ConnectionSupport {
         }
     }
 
-    /**
-     * In GnuR a connection is just an numeric vector and can be created by setting the class, even
-     * if the result isn't actually a valid connection, e.g.
-     * {@code structure(2, class=c("terminal","connection"))}.
-     */
-    public static RConnection fromVector(RVector<?> vector, @SuppressWarnings("unused") RStringVector classAttr) {
-        int index = RRuntime.asInteger(vector);
-        RConnection result = RContext.getInstance().stateRConnection.getConnection(index);
-        if (result == null) {
-            // non-existent connection, still legal according to GnuR
-            // we cannot throw an error here as this can be used by parallel packages - do it lazily
-            return InvalidConnection.instance;
-        }
-        return result;
-    }
-
     // TODO implement all open modes
 
     public static final class InvalidConnection extends RConnection {
@@ -521,6 +511,8 @@ public class ConnectionSupport {
          */
         private int descriptor;
 
+        private final ConnectionClass conClass;
+
         /**
          * The constructor for every connection class except {@link StdConnections}.
          *
@@ -548,13 +540,12 @@ public class ConnectionSupport {
          * Primitive constructor that just assigns state.
          */
         private BaseRConnection(ConnectionClass conClass, OpenMode mode) {
+            this.conClass = conClass;
             this.openMode = mode;
-            String[] classes = new String[2];
-            classes[0] = conClass.printName;
-            classes[1] = "connection";
-            initAttributes().put(RRuntime.CLASS_ATTR_KEY, RDataFactory.createStringVector(classes, RDataFactory.COMPLETE_VECTOR));
-            // For GnuR compatibility we define the "conn_id" attribute
-            getAttributes().put("conn_id", RDataFactory.createExternalPtr(0, RDataFactory.createSymbol("connection")));
+        }
+
+        public final ConnectionClass getConnectionClass() {
+            return conClass;
         }
 
         protected void openNonLazyConnection() throws IOException {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
index 4939a1c008c1c6cd9d4840df95e5b3f1441205df..8717d9a2d794aecdc40c3fe83c2116415b1c127d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
@@ -29,40 +29,45 @@ import java.nio.ByteBuffer;
 import java.util.LinkedList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RAttributeStorage;
-import com.oracle.truffle.r.runtime.data.RInteger;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RShareable;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributes;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
  * Denotes an R {@code connection} instance used in the {@code base} I/O library.
  *
  * TODO Refactor the pushBack code into ConnectionsSupport
  */
-public abstract class RConnection extends RAttributeStorage implements AutoCloseable, RAbstractContainer {
+public abstract class RConnection implements AutoCloseable {
 
-    @Override
-    public RStringVector getImplicitClass() {
-        throw RInternalError.shouldNotReachHere("no implicit class for connections");
+    public final RAbstractIntVector asVector() {
+        String[] classes = new String[]{ConnectionSupport.getBaseConnection(this).getConnectionClass().getPrintName(), "connection"};
+
+        RAbstractIntVector result = RDataFactory.createIntVector(new int[]{getDescriptor()}, true);
+
+        RStringVector classVector = RDataFactory.createStringVector(classes, RDataFactory.COMPLETE_VECTOR);
+        // it's important to put "this" into the externalptr, so that it doesn't get collected
+        RExternalPtr connectionId = RDataFactory.createExternalPtr(0, this, RDataFactory.createSymbol("connection"), RNull.instance);
+        result.initAttributes(RAttributes.createInitialized(new String[]{RRuntime.CLASS_ATTR_KEY, "conn_id"}, new Object[]{classVector, connectionId}));
+
+        return result;
+    }
+
+    public static BaseRConnection fromIndex(int con) {
+        return RContext.getInstance().stateRConnection.getConnection(con, true);
     }
 
     private LinkedList<String> pushBack;
 
     public abstract String[] readLinesInternal(int n, boolean warn, boolean skipNul) throws IOException;
 
-    @Override
-    public RType getRType() {
-        return RType.Integer;
-    }
-
     private String readOneLineWithPushBack(String[] res, int ind, @SuppressWarnings("unused") boolean warn, @SuppressWarnings("unused") boolean skipNul) {
         String s = pushBack.pollLast();
         if (s == null) {
@@ -206,7 +211,7 @@ public abstract class RConnection extends RAttributeStorage implements AutoClose
      * Pushes lines back to the connection.
      */
     @TruffleBoundary
-    public void pushBack(RAbstractStringVector lines, boolean addNewLine) {
+    public final void pushBack(RAbstractStringVector lines, boolean addNewLine) {
         if (pushBack == null) {
             pushBack = new LinkedList<>();
         }
@@ -223,7 +228,7 @@ public abstract class RConnection extends RAttributeStorage implements AutoClose
      * Return the length of the push back.
      */
     @TruffleBoundary
-    public int pushBackLength() {
+    public final int pushBackLength() {
         return pushBack == null ? 0 : pushBack.size();
     }
 
@@ -231,7 +236,7 @@ public abstract class RConnection extends RAttributeStorage implements AutoClose
      * Clears the pushback.
      */
     @TruffleBoundary
-    public void pushBackClear() {
+    public final void pushBackClear() {
         pushBack = null;
     }
 
@@ -330,89 +335,4 @@ public abstract class RConnection extends RAttributeStorage implements AutoClose
      */
     public abstract boolean isOpen();
 
-    /*
-     * Methods from the RAbstractContainer interface, which is implemented to allow an RVector<?>to
-     * transform to an RConnection via a class update.
-     */
-
-    @Override
-    public boolean isComplete() {
-        return true;
-    }
-
-    @Override
-    public int getLength() {
-        return 1;
-    }
-
-    @Override
-    public RAbstractContainer resize(int size) {
-        return this;
-    }
-
-    @Override
-    public boolean hasDimensions() {
-        return false;
-    }
-
-    @Override
-    public int[] getDimensions() {
-        return null;
-    }
-
-    @Override
-    public void setDimensions(int[] newDimensions) {
-    }
-
-    @Override
-    public Class<?> getElementClass() {
-        return RInteger.class;
-    }
-
-    @Override
-    public RTypedValue getNonShared() {
-        return this;
-    }
-
-    @Override
-    public RShareable materializeToShareable() {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    @Override
-    public Object getDataAtAsObject(int index) {
-        return getDescriptor();
-    }
-
-    @Override
-    public RStringVector getNames(RAttributeProfiles attrProfiles) {
-        return null;
-    }
-
-    @Override
-    public void setNames(RStringVector newNames) {
-    }
-
-    @Override
-    public RList getDimNames(RAttributeProfiles attrProfiles) {
-        return null;
-    }
-
-    @Override
-    public void setDimNames(RList newDimNames) {
-    }
-
-    @Override
-    public Object getRowNames(RAttributeProfiles attrProfiles) {
-        return null;
-    }
-
-    @Override
-    public void setRowNames(RAbstractVector rowNames) {
-    }
-
-    @Override
-    public boolean isObject(RAttributeProfiles attrProfiles) {
-        return true;
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index 85654a5efd1041d957fdf4a4400553e48391aba0..1e913660559bcba80f69f54adfa20024f8881234 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -494,12 +494,21 @@ public final class RDataFactory {
         return traceDataCreated(new RS4Object());
     }
 
+    public static RExternalPtr createExternalPtr(long value, Object externalObject, Object tag, Object prot) {
+        assert tag != null : "null tag, use RNull.instance instead";
+        assert prot != null : "null prot, use RNull.instance instead";
+        return traceDataCreated(new RExternalPtr(value, externalObject, tag, prot));
+    }
+
     public static RExternalPtr createExternalPtr(long value, Object tag, Object prot) {
-        return traceDataCreated(new RExternalPtr(value, tag, prot));
+        assert tag != null : "null tag, use RNull.instance instead";
+        assert prot != null : "null prot, use RNull.instance instead";
+        return traceDataCreated(new RExternalPtr(value, null, tag, prot));
     }
 
     public static RExternalPtr createExternalPtr(long value, Object tag) {
-        return traceDataCreated(new RExternalPtr(value, tag, RNull.instance));
+        assert tag != null : "null tag, use RNull.instance instead";
+        return traceDataCreated(new RExternalPtr(value, null, tag, RNull.instance));
     }
 
     /*
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExternalPtr.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExternalPtr.java
index d1589dbcad80692a9ac7a422b691bd39e5a972e4..1d4124eaabefee77ed3e519acc7be4cf668a5044 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExternalPtr.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExternalPtr.java
@@ -31,21 +31,27 @@ public class RExternalPtr extends RAttributeStorage {
     private long addr;
     private Object tag;
     private Object prot;
+    private Object externalObject;
 
-    RExternalPtr(long addr, Object tag, Object prot) {
+    RExternalPtr(long addr, Object externalObject, Object tag, Object prot) {
         this.addr = addr;
+        this.externalObject = externalObject;
         this.tag = tag;
         this.prot = prot;
     }
 
     public RExternalPtr copy() {
-        return RDataFactory.createExternalPtr(addr, tag, prot);
+        return RDataFactory.createExternalPtr(addr, externalObject, tag, prot);
     }
 
     public long getAddr() {
         return addr;
     }
 
+    public Object getExternalObject() {
+        return externalObject;
+    }
+
     public Object getTag() {
         return tag;
     }
@@ -58,6 +64,10 @@ public class RExternalPtr extends RAttributeStorage {
         this.addr = value;
     }
 
+    public void setExternalObject(Object externalObject) {
+        this.externalObject = externalObject;
+    }
+
     public void setTag(Object tag) {
         this.tag = tag;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java
index 5a7d42fb165f105cf7ca9b84338aa7ab275c2b08..df4552dc03dcb5b2d10cb73346c3a01255848701 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.api.dsl.TypeSystem;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
@@ -50,7 +49,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 @TypeSystem({boolean.class, byte.class, int.class, double.class, RRaw.class, RComplex.class, String.class, RIntSequence.class, RDoubleSequence.class, RIntVector.class, RDoubleVector.class,
                 RRawVector.class, RComplexVector.class, RStringVector.class, RLogicalVector.class, RFunction.class, RNull.class, RMissing.class, REmpty.class, REnvironment.class, RExpression.class,
-                RConnection.class, MaterializedFrame.class, FrameSlot.class, RAbstractIntVector.class, RAbstractDoubleVector.class, RAbstractLogicalVector.class, RAbstractComplexVector.class,
+                MaterializedFrame.class, FrameSlot.class, RAbstractIntVector.class, RAbstractDoubleVector.class, RAbstractLogicalVector.class, RAbstractComplexVector.class,
                 RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RSymbol.class, RPromise.class, RLanguage.class,
                 RPairList.class, RExternalPtr.class, RS4Object.class, RAbstractContainer.class, RAttributable.class, RArgsValuesAndNames.class, RTypedValue.class, RType.class, Object[].class})
 public class RTypes {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index 2768eba5bde538d2410ea087faf6eed0ee447056..d9c60d45c7389b664d87c61e1a02210504415230 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -32,7 +32,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.SuppressFBWarnings;
-import com.oracle.truffle.r.runtime.conn.ConnectionSupport;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -554,9 +553,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
                         // valid classes when it reaches this error.
                         throw RError.error(RError.SHOW_CALLER2, RError.Message.ADDING_INVALID_CLASS, "factor");
                     }
-                } else if (RType.Connection.getName().equals(attr)) {
-                    // convert to RConnection
-                    return ConnectionSupport.fromVector(vector, classAttr);
                 }
             }
             vector.putAttribute(RRuntime.CLASS_ATTR_KEY, classAttr);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java
index c54c22481c70296eb456a42c4e6f7f1ba73d5d6f..f5ba98ae711c1c7666c33867a32e731d850ea38a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrumentation.Instrumentable;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -211,10 +210,6 @@ public abstract class RNode extends RBaseNode implements RInstrumentableNode {
         return RTypesGen.expectREnvironment(execute(frame));
     }
 
-    public RConnection executeRConnection(VirtualFrame frame) throws UnexpectedResultException {
-        return RTypesGen.expectRConnection(execute(frame));
-    }
-
     public RExpression executeRExpression(VirtualFrame frame) throws UnexpectedResultException {
         return RTypesGen.expectRExpression(execute(frame));
     }