diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index 090cd5aa987d71ee1ac1da2c7155bc2e7643cdd2..5a9c503079500a97603e9ffdb595104feaa5314e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -62,6 +62,7 @@ public enum FastROptions {
     EmitTmpHashed("Use an SHA-256 hash as file name to reduce temporary file creation.", true),
     SpawnUsesPolyglot("use PolyglotEngine for .fastr.context.spwan", false),
     SynchronizeNativeCode("allow only one thread to enter packages' native code", false),
+    ForeignObjectWrappers("use wrappers for foreign objects (as opposed to full conversion)", false),
 
     // Promises optimizations
     EagerEval("If enabled, overrides all other EagerEval switches (see EagerEvalHelper)", false),
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignBooleanWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignBooleanWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3c30b09276c0864281b5cbd856c0b8cb0c876ea
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignBooleanWrapper.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.data.nodes.FastPathVectorAccess.FastPathFromLogicalAccess;
+import com.oracle.truffle.r.runtime.data.nodes.SlowPathVectorAccess.SlowPathFromLogicalAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+
+public final class RForeignBooleanWrapper extends RForeignWrapper implements RAbstractLogicalVector {
+
+    public RForeignBooleanWrapper(TruffleObject delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public RLogicalVector materialize() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAtAsObject(int index) {
+        return getDataAt(index);
+    }
+
+    @Override
+    @TruffleBoundary
+    public byte getDataAt(int index) {
+        try {
+            return RRuntime.asLogical((boolean) ForeignAccess.sendRead(READ, delegate, index));
+        } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    private static final class FastPathAccess extends FastPathFromLogicalAccess {
+
+        FastPathAccess(RAbstractContainer value) {
+            super(value);
+        }
+
+        @Child private Node getSize = Message.GET_SIZE.createNode();
+        @Child private Node read = Message.READ.createNode();
+
+        @Override
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(getSize, ((RForeignWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected byte getLogical(Object internalStore, int index) {
+            try {
+                return RRuntime.asLogical((boolean) ForeignAccess.sendRead(read, (TruffleObject) internalStore, index));
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public VectorAccess access() {
+        return new FastPathAccess(this);
+    }
+
+    private static final SlowPathFromLogicalAccess SLOW_PATH_ACCESS = new SlowPathFromLogicalAccess() {
+        @Override
+        @TruffleBoundary
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(GET_SIZE, ((RForeignBooleanWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected byte getLogical(Object store, int index) {
+            RForeignBooleanWrapper vector = (RForeignBooleanWrapper) store;
+            try {
+                return RRuntime.asLogical((boolean) ForeignAccess.sendRead(READ, vector.delegate, index));
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    };
+
+    @Override
+    public VectorAccess slowPathAccess() {
+        return SLOW_PATH_ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignDoubleWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignDoubleWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..90dbbe7faf6395e88a163bef390b916e28a1bf28
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignDoubleWrapper.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.nodes.FastPathVectorAccess.FastPathFromDoubleAccess;
+import com.oracle.truffle.r.runtime.data.nodes.SlowPathVectorAccess.SlowPathFromDoubleAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+
+public final class RForeignDoubleWrapper extends RForeignWrapper implements RAbstractDoubleVector {
+
+    public RForeignDoubleWrapper(TruffleObject delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public RDoubleVector materialize() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAtAsObject(int index) {
+        return getDataAt(index);
+    }
+
+    @Override
+    @TruffleBoundary
+    public double getDataAt(int index) {
+        try {
+            return ((Number) ForeignAccess.sendRead(READ, delegate, index)).doubleValue();
+        } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    private static final class FastPathAccess extends FastPathFromDoubleAccess {
+
+        FastPathAccess(RAbstractContainer value) {
+            super(value);
+        }
+
+        private final ValueProfile resultProfile = ValueProfile.createClassProfile();
+        @Child private Node getSize = Message.GET_SIZE.createNode();
+        @Child private Node read = Message.READ.createNode();
+
+        @Override
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(getSize, ((RForeignWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected double getDouble(Object internalStore, int index) {
+            try {
+                return ((Number) resultProfile.profile(ForeignAccess.sendRead(read, (TruffleObject) internalStore, index))).doubleValue();
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public VectorAccess access() {
+        return new FastPathAccess(this);
+    }
+
+    private static final SlowPathFromDoubleAccess SLOW_PATH_ACCESS = new SlowPathFromDoubleAccess() {
+        @Override
+        @TruffleBoundary
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(GET_SIZE, ((RForeignDoubleWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected double getDouble(Object store, int index) {
+            RForeignDoubleWrapper vector = (RForeignDoubleWrapper) store;
+            try {
+                return ((Number) ForeignAccess.sendRead(READ, vector.delegate, index)).doubleValue();
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    };
+
+    @Override
+    public VectorAccess slowPathAccess() {
+        return SLOW_PATH_ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignIntWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignIntWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..68c4ca07ead520b8c62cc97ba41b58f6a5b94e98
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignIntWrapper.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.runtime.RInternalError;
+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.nodes.FastPathVectorAccess.FastPathFromIntAccess;
+import com.oracle.truffle.r.runtime.data.nodes.SlowPathVectorAccess.SlowPathFromIntAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+
+public final class RForeignIntWrapper extends RForeignWrapper implements RAbstractIntVector {
+
+    public RForeignIntWrapper(TruffleObject delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public RIntVector materialize() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAtAsObject(int index) {
+        return getDataAt(index);
+    }
+
+    @Override
+    @TruffleBoundary
+    public int getDataAt(int index) {
+        try {
+            return ((Number) ForeignAccess.sendRead(READ, delegate, index)).intValue();
+        } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    private static final class FastPathAccess extends FastPathFromIntAccess {
+
+        FastPathAccess(RAbstractContainer value) {
+            super(value);
+        }
+
+        private final ValueProfile resultProfile = ValueProfile.createClassProfile();
+        @Child private Node getSize = Message.GET_SIZE.createNode();
+        @Child private Node read = Message.READ.createNode();
+
+        @Override
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(getSize, ((RForeignWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected int getInt(Object internalStore, int index) {
+            try {
+                return ((Number) resultProfile.profile(ForeignAccess.sendRead(read, (TruffleObject) internalStore, index))).intValue();
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public VectorAccess access() {
+        return new FastPathAccess(this);
+    }
+
+    private static final SlowPathFromIntAccess SLOW_PATH_ACCESS = new SlowPathFromIntAccess() {
+        @Override
+        @TruffleBoundary
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(GET_SIZE, ((RForeignIntWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected int getInt(Object store, int index) {
+            RForeignIntWrapper vector = (RForeignIntWrapper) store;
+            try {
+                return ((Number) ForeignAccess.sendRead(READ, vector.delegate, index)).intValue();
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    };
+
+    @Override
+    public VectorAccess slowPathAccess() {
+        return SLOW_PATH_ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignListWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignListWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a67f4c389310fd510d10f658549f03a3d35d072
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignListWrapper.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
+import com.oracle.truffle.r.runtime.data.nodes.FastPathVectorAccess.FastPathFromListAccess;
+import com.oracle.truffle.r.runtime.data.nodes.SlowPathVectorAccess.SlowPathFromListAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+
+public final class RForeignListWrapper extends RForeignWrapper implements RAbstractListVector {
+
+    public RForeignListWrapper(TruffleObject delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public RList materialize() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAtAsObject(int index) {
+        return getDataAt(index);
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAt(int index) {
+        try {
+            return ForeignAccess.sendRead(READ, delegate, index);
+        } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    private static final class FastPathAccess extends FastPathFromListAccess {
+
+        FastPathAccess(RAbstractContainer value) {
+            super(value);
+        }
+
+        @Child private Node getSize = Message.GET_SIZE.createNode();
+        @Child private Node read = Message.READ.createNode();
+
+        @Override
+        public RType getType() {
+            return RType.List;
+        }
+
+        @Override
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(getSize, ((RForeignWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected Object getListElement(Object internalStore, int index) {
+            try {
+                return ForeignAccess.sendRead(read, (TruffleObject) internalStore, index);
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public VectorAccess access() {
+        return new FastPathAccess(this);
+    }
+
+    private static final SlowPathFromListAccess SLOW_PATH_ACCESS = new SlowPathFromListAccess() {
+        @Override
+        public RType getType() {
+            return RType.List;
+        }
+
+        @Override
+        @TruffleBoundary
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(GET_SIZE, ((RForeignListWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected Object getListElement(Object store, int index) {
+            RForeignListWrapper vector = (RForeignListWrapper) store;
+            try {
+                return ForeignAccess.sendRead(READ, vector.delegate, index);
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    };
+
+    @Override
+    public VectorAccess slowPathAccess() {
+        return SLOW_PATH_ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignNamedListWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignNamedListWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c3be432639391b85ea7362c6c8bb0bc178392d2
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignNamedListWrapper.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
+import com.oracle.truffle.r.runtime.data.nodes.FastPathVectorAccess.FastPathFromListAccess;
+import com.oracle.truffle.r.runtime.data.nodes.SlowPathVectorAccess.SlowPathFromListAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
+
+public final class RForeignNamedListWrapper extends RForeignWrapper implements RAbstractListVector {
+
+    private final RStringVector names;
+
+    public RForeignNamedListWrapper(TruffleObject delegate, RStringVector names) {
+        super(delegate);
+        this.names = names;
+    }
+
+    @Override
+    public Object getInternalStore() {
+        return this;
+    }
+
+    @Override
+    public int getLength() {
+        return names.getLength();
+    }
+
+    @Override
+    public RStringVector getNames() {
+        return names;
+    }
+
+    @Override
+    public RList materialize() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAtAsObject(int index) {
+        return getDataAt(index);
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAt(int index) {
+        try {
+            return FOREIGN_TO_R.execute(ForeignAccess.sendRead(READ, delegate, names.getDataAt(index)));
+        } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    private static final class FastPathAccess extends FastPathFromListAccess {
+
+        FastPathAccess(RAbstractContainer value) {
+            super(value);
+        }
+
+        @Child private Node getSize = Message.GET_SIZE.createNode();
+        @Child private Node read = Message.READ.createNode();
+        @Child private Foreign2R foreign2r = Foreign2R.create();
+
+        @Override
+        public RType getType() {
+            return RType.List;
+        }
+
+        @Override
+        protected int getLength(RAbstractContainer vector) {
+            return ((RForeignNamedListWrapper) vector).getLength();
+        }
+
+        @Override
+        protected Object getListElement(Object internalStore, int index) {
+            try {
+                RForeignNamedListWrapper wrapper = (RForeignNamedListWrapper) internalStore;
+                return foreign2r.execute(ForeignAccess.sendRead(read, wrapper.delegate, wrapper.names.getDataAt(index)));
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public VectorAccess access() {
+        return new FastPathAccess(this);
+    }
+
+    private static final Foreign2R FOREIGN_TO_R = Foreign2R.create();
+
+    private static final SlowPathFromListAccess SLOW_PATH_ACCESS = new SlowPathFromListAccess() {
+        @Override
+        public RType getType() {
+            return RType.List;
+        }
+
+        @Override
+        @TruffleBoundary
+        protected int getLength(RAbstractContainer vector) {
+            return ((RForeignNamedListWrapper) vector).names.getLength();
+        }
+
+        @Override
+        protected Object getListElement(Object store, int index) {
+            RForeignNamedListWrapper vector = (RForeignNamedListWrapper) store;
+            try {
+                return FOREIGN_TO_R.execute(ForeignAccess.sendRead(READ, vector.delegate, vector.names.getDataAt(index)));
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    };
+
+    @Override
+    public VectorAccess slowPathAccess() {
+        return SLOW_PATH_ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignStringWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignStringWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..148bb9cf13a968cb98a128ef7cf69d20a3d21c8f
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignStringWrapper.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.nodes.FastPathVectorAccess.FastPathFromStringAccess;
+import com.oracle.truffle.r.runtime.data.nodes.SlowPathVectorAccess.SlowPathFromStringAccess;
+import com.oracle.truffle.r.runtime.data.nodes.VectorAccess;
+
+public final class RForeignStringWrapper extends RForeignWrapper implements RAbstractStringVector {
+
+    public RForeignStringWrapper(TruffleObject delegate) {
+        super(delegate);
+    }
+
+    @Override
+    public RStringVector materialize() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object getDataAtAsObject(int index) {
+        return getDataAt(index);
+    }
+
+    @Override
+    @TruffleBoundary
+    public String getDataAt(int index) {
+        try {
+            return ForeignAccess.sendRead(READ, delegate, index).toString();
+        } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    private static final class FastPathAccess extends FastPathFromStringAccess {
+
+        FastPathAccess(RAbstractContainer value) {
+            super(value);
+        }
+
+        private final ValueProfile resultProfile = ValueProfile.createClassProfile();
+        @Child private Node getSize = Message.GET_SIZE.createNode();
+        @Child private Node read = Message.READ.createNode();
+
+        @Override
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(getSize, ((RForeignWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        protected String getString(Object internalStore, int index) {
+            try {
+                return resultProfile.profile(ForeignAccess.sendRead(read, (TruffleObject) internalStore, index)).toString();
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public VectorAccess access() {
+        return new FastPathAccess(this);
+    }
+
+    private static final SlowPathFromStringAccess SLOW_PATH_ACCESS = new SlowPathFromStringAccess() {
+        @Override
+        @TruffleBoundary
+        protected int getLength(RAbstractContainer vector) {
+            try {
+                return (int) ForeignAccess.sendGetSize(GET_SIZE, ((RForeignStringWrapper) vector).delegate);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+
+        @Override
+        @TruffleBoundary
+        protected String getString(Object store, int index) {
+            RForeignStringWrapper vector = (RForeignStringWrapper) store;
+            try {
+                return ForeignAccess.sendRead(READ, vector.delegate, index).toString();
+            } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    };
+
+    @Override
+    public VectorAccess slowPathAccess() {
+        return SLOW_PATH_ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..79350c7cb3ec54287815eff5219e15f80f6004f4
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RForeignWrapper.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class RForeignWrapper implements RAbstractVector {
+
+    protected static final Node GET_SIZE = Message.GET_SIZE.createNode();
+    protected static final Node READ = Message.READ.createNode();
+
+    protected final TruffleObject delegate;
+
+    protected RForeignWrapper(TruffleObject delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    @TruffleBoundary
+    public final int getLength() {
+        try {
+            return (int) ForeignAccess.sendGetSize(GET_SIZE, delegate);
+        } catch (UnsupportedMessageException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
+    @Override
+    public final RAbstractContainer resize(int size) {
+        return materialize().resize(size);
+    }
+
+    @Override
+    public final boolean isComplete() {
+        return true;
+    }
+
+    @Override
+    public final void setComplete(boolean complete) {
+        // sequences are always complete
+    }
+
+    @Override
+    public final boolean hasDimensions() {
+        return false;
+    }
+
+    @Override
+    public final int[] getDimensions() {
+        return null;
+    }
+
+    @Override
+    public final void setDimensions(int[] newDimensions) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RAbstractVector copy() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RAbstractVector copyDropAttributes() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RAbstractVector copyWithNewDimensions(int[] newDimensions) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RStringVector getNames() {
+        return null;
+    }
+
+    @Override
+    public final void setNames(RStringVector newNames) {
+        // should only be used on materialized sequence
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RList getDimNames() {
+        return null;
+    }
+
+    @Override
+    public final void setDimNames(RList newDimNames) {
+        // should only be used on materialized sequence
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final Object getRowNames() {
+        return RNull.instance;
+    }
+
+    @Override
+    public final void setRowNames(RAbstractVector rowNames) {
+        // should only be used on materialized sequence
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final DynamicObject initAttributes() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final void initAttributes(DynamicObject newAttributes) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final DynamicObject getAttributes() {
+        return null;
+    }
+
+    @Override
+    public final boolean isMatrix() {
+        return false;
+    }
+
+    @Override
+    public final boolean isArray() {
+        return false;
+    }
+
+    @Override
+    public final boolean isObject() {
+        return false;
+    }
+
+    @Override
+    public final RTypedValue getNonShared() {
+        return materialize().getNonShared();
+    }
+
+    @Override
+    public final int getTypedValueInfo() {
+        return 0;
+    }
+
+    @Override
+    public final void setTypedValueInfo(int value) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final boolean isS4() {
+        return false;
+    }
+
+    @Override
+    public final Object getInternalStore() {
+        return delegate;
+    }
+
+    @Override
+    public final RVector<?> copyResized(int size, boolean fillNA) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RVector<?> copyResizedWithDimensions(int[] newDimensions, boolean fillNA) {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public final RVector<?> createEmptySameType(int newLength, boolean newIsComplete) {
+        throw RInternalError.shouldNotReachHere();
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java
index bb59cfc10cae05de573a6806be5509dc8c48a24c..43bffb637f5f30e311b4d1be0b663c15ddab6921 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/ForeignArray2R.java
@@ -43,9 +43,16 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.interop.UnsupportedTypeException;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.FastROptions;
 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.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RForeignBooleanWrapper;
+import com.oracle.truffle.r.runtime.data.RForeignDoubleWrapper;
+import com.oracle.truffle.r.runtime.data.RForeignIntWrapper;
+import com.oracle.truffle.r.runtime.data.RForeignListWrapper;
+import com.oracle.truffle.r.runtime.data.RForeignStringWrapper;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
@@ -59,9 +66,10 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 public abstract class ForeignArray2R extends RBaseNode {
 
     @Child protected Node hasSize = Message.HAS_SIZE.createNode();
+    @Child protected Node getSize = Message.GET_SIZE.createNode();
     @Child private Foreign2R foreign2R;
     @Child private ForeignArray2R foreignArray2R;
-    @Child private Node read;
+    @Child private Node read = Message.READ.createNode();
     @Child private Node isNull;
     @Child private Node isBoxed;
     @Child private Node unbox;
@@ -91,25 +99,54 @@ public abstract class ForeignArray2R extends RBaseNode {
      *
      */
     public Object convert(Object obj, boolean recursive) {
-        Object result = execute(obj, recursive, null, 0);
-        if (result instanceof ForeignArrayData) {
-            ForeignArrayData arrayData = (ForeignArrayData) result;
-            if (arrayData.elements.isEmpty()) {
-                return RDataFactory.createList();
+        if (FastROptions.ForeignObjectWrappers.getBooleanValue()) {
+            if (isForeignArray(obj)) {
+                TruffleObject truffleObject = (TruffleObject) obj;
+                try {
+                    int size = (int) ForeignAccess.sendGetSize(getSize, truffleObject);
+                    if (size == 0) {
+                        return new RForeignListWrapper(truffleObject);
+                    } else {
+                        Object firstElement = ForeignAccess.sendRead(read, truffleObject, 0);
+                        switch (InteropTypeCheck.determineType(firstElement)) {
+                            case BOOLEAN:
+                                return new RForeignBooleanWrapper(truffleObject);
+                            case DOUBLE:
+                                return new RForeignDoubleWrapper(truffleObject);
+                            case INTEGER:
+                                return new RForeignIntWrapper(truffleObject);
+                            case STRING:
+                                return new RForeignStringWrapper(truffleObject);
+                            default:
+                                return new RForeignListWrapper(truffleObject);
+                        }
+                    }
+                } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                    throw RInternalError.shouldNotReachHere(e);
+                }
+            } else {
+                return obj;
             }
-            return asAbstractVector(arrayData);
+        } else {
+            Object result = execute(obj, recursive, null, 0);
+            if (result instanceof ForeignArrayData) {
+                ForeignArrayData arrayData = (ForeignArrayData) result;
+                if (arrayData.elements.isEmpty()) {
+                    return RDataFactory.createList();
+                }
+                return asAbstractVector(arrayData);
+            }
+            return result;
         }
-        return result;
     }
 
     protected abstract Object execute(Object obj, boolean recursive, ForeignArrayData arrayData, int depth);
 
     @Specialization(guards = {"isForeignArray(obj)"})
     @TruffleBoundary
-    protected ForeignArrayData doArray(TruffleObject obj, boolean recursive, ForeignArrayData arrayData, int depth,
-                    @Cached("GET_SIZE.createNode()") Node getSize) {
+    protected ForeignArrayData doArray(TruffleObject obj, boolean recursive, ForeignArrayData arrayData, int depth) {
         try {
-            return collectArrayElements(arrayData == null ? new ForeignArrayData() : arrayData, obj, recursive, getSize, depth);
+            return collectArrayElements(arrayData == null ? new ForeignArrayData() : arrayData, obj, recursive, depth);
         } catch (UnsupportedMessageException | UnknownIdentifierException e) {
             throw error(RError.Message.GENERIC, "error while converting array: " + e.getMessage());
         }
@@ -132,7 +169,7 @@ public abstract class ForeignArray2R extends RBaseNode {
         return obj;
     }
 
-    private ForeignArrayData collectArrayElements(ForeignArrayData arrayData, TruffleObject obj, boolean recursive, Node getSize, int depth)
+    private ForeignArrayData collectArrayElements(ForeignArrayData arrayData, TruffleObject obj, boolean recursive, int depth)
                     throws UnsupportedMessageException, UnknownIdentifierException {
         int size = (int) ForeignAccess.sendGetSize(getSize, obj);
 
@@ -152,10 +189,6 @@ public abstract class ForeignArray2R extends RBaseNode {
             return arrayData;
         }
         for (int i = 0; i < size; i++) {
-            if (read == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                read = insert(Message.READ.createNode());
-            }
             Object element = ForeignAccess.sendRead(read, obj, i);
             if (recursive && (isForeignArray(element, hasSize) || isJavaIterable(element))) {
                 recurse(arrayData, element, depth);
@@ -387,6 +420,20 @@ public abstract class ForeignArray2R extends RBaseNode {
 
         private RType type = null;
 
+        public static RType determineType(Object value) {
+            if (value instanceof Boolean) {
+                return RType.BOOLEAN;
+            } else if (value instanceof Byte || value instanceof Integer || value instanceof Short) {
+                return RType.INTEGER;
+            } else if (value instanceof Double || value instanceof Float || value instanceof Long) {
+                return RType.DOUBLE;
+            } else if (value instanceof Character || value instanceof String) {
+                return RType.STRING;
+            } else {
+                return RType.NONE;
+            }
+        }
+
         public RType checkForeign(Object value) {
             if (value instanceof Boolean) {
                 setType(RType.BOOLEAN);