diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java
index 7ea156016dc9d6352903d006d7200915237019de..5aa08ac4faab867795d867b1cb21b8cafabef269 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java
@@ -60,6 +60,13 @@ public class ActiveBindingMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class ActiveBindingKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object idx) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class ActiveBindingCheck extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ListMR.java
new file mode 100644
index 0000000000000000000000000000000000000000..5dad942ddc4384657e394bcbaa4d3ce48f0734a6
--- /dev/null
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ListMR.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2016, 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.engine.interop;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.KeyInfo;
+import com.oracle.truffle.api.interop.KeyInfo.Builder;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.engine.interop.ListMRFactory.ListKeyInfoImplNodeGen;
+import com.oracle.truffle.r.engine.interop.ListMRFactory.ListReadImplNodeGen;
+import com.oracle.truffle.r.engine.interop.ListMRFactory.ListWriteImplNodeGen;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
+import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
+import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
+import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.control.RLengthNode;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RLogical;
+import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
+import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
+
+public class ListMR {
+
+    @MessageResolution(receiverType = RList.class)
+    public static class RListMR extends ListMR {
+        @Resolve(message = "IS_BOXED")
+        public abstract static class RListIsBoxedNode extends Node {
+            protected Object access(RList receiver) {
+                return isBoxed(receiver);
+            }
+        }
+
+        @Resolve(message = "HAS_SIZE")
+        public abstract static class RListHasSizeNode extends Node {
+            protected Object access(RList receiver) {
+                return hasSize(receiver);
+            }
+        }
+
+        @Resolve(message = "GET_SIZE")
+        public abstract static class RListGetSizeNode extends Node {
+            @Child private RLengthNode lengthNode = RLengthNode.create();
+
+            protected Object access(VirtualFrame frame, RList receiver) {
+                return getSize(frame, receiver, lengthNode);
+            }
+        }
+
+        @Resolve(message = "IS_NULL")
+        public abstract static class RListIsNullNode extends Node {
+            protected Object access(RList receiver) {
+                return isNull(receiver);
+            }
+        }
+
+        @Resolve(message = "READ")
+        public abstract static class RListReadNode extends Node {
+            @Child private ListReadImplNode read = ListReadImplNodeGen.create();
+
+            protected Object access(VirtualFrame frame, RList receiver, Object identifier) {
+                return read.execute(frame, receiver, identifier);
+            }
+        }
+
+        @Resolve(message = "WRITE")
+        public abstract static class RListWriteNode extends Node {
+            @Child private ListWriteImplNode writeNode = ListWriteImplNodeGen.create();
+
+            protected Object access(VirtualFrame frame, RList receiver, Object identifier, Object valueObj) {
+                return writeNode.execute(frame, receiver, identifier, valueObj);
+            }
+        }
+
+        @Resolve(message = "KEYS")
+        public abstract static class RListKeysNode extends Node {
+            @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
+
+            protected Object access(RList receiver) {
+                return listKeys(receiver, getNamesNode);
+            }
+        }
+
+        @Resolve(message = "KEY_INFO")
+        public abstract static class RListKeyInfoNode extends Node {
+            @Child private ListKeyInfoImplNode keyInfoNode = ListKeyInfoImplNodeGen.create();
+
+            protected Object access(VirtualFrame frame, TruffleObject receiver, Object idx) {
+                return keyInfoNode.execute(frame, receiver, idx);
+            }
+        }
+
+        @Resolve(message = "TO_NATIVE")
+        public abstract static class RListToNativeNode extends Node {
+            protected Object access(RTruffleObject receiver) {
+                return toNativePointer(receiver);
+            }
+        }
+
+        @CanResolve
+        public abstract static class RListCheck extends Node {
+
+            protected static boolean test(TruffleObject receiver) {
+                return receiver instanceof RList;
+            }
+        }
+    }
+
+    @MessageResolution(receiverType = RPairList.class)
+    public static class RPairListMR {
+        @Resolve(message = "IS_BOXED")
+        public abstract static class RPairListIsBoxedNode extends Node {
+            protected Object access(RPairList receiver) {
+                return isBoxed(receiver);
+            }
+        }
+
+        @Resolve(message = "HAS_SIZE")
+        public abstract static class RPairListHasSizeNode extends Node {
+            protected Object access(RPairList receiver) {
+                return hasSize(receiver);
+            }
+        }
+
+        @Resolve(message = "GET_SIZE")
+        public abstract static class RPairListGetSizeNode extends Node {
+            @Child private RLengthNode lengthNode = RLengthNode.create();
+
+            protected Object access(VirtualFrame frame, RPairList receiver) {
+                return getSize(frame, receiver, lengthNode);
+            }
+        }
+
+        @Resolve(message = "IS_NULL")
+        public abstract static class RPairListIsNullNode extends Node {
+            protected Object access(RPairList receiver) {
+                return isNull(receiver);
+            }
+        }
+
+        @Resolve(message = "IS_EXECUTABLE")
+        public abstract static class RPairListIsExecutableNode extends Node {
+            protected Object access(@SuppressWarnings("unused") RPairList receiver) {
+                return false;
+            }
+        }
+
+        @Resolve(message = "READ")
+        public abstract static class RPairListReadNode extends Node {
+            @Child private ListReadImplNode read = ListReadImplNodeGen.create();
+
+            protected Object access(VirtualFrame frame, RPairList receiver, Object identifier) {
+                return read.execute(frame, receiver, identifier);
+            }
+        }
+
+        @Resolve(message = "WRITE")
+        public abstract static class RPairListWriteNode extends Node {
+            @Child private ListWriteImplNode writeNode = ListWriteImplNodeGen.create();
+
+            protected Object access(VirtualFrame frame, RPairList receiver, Object identifier, Object valueObj) {
+                return writeNode.execute(frame, receiver, identifier, valueObj);
+            }
+        }
+
+        @Resolve(message = "KEYS")
+        public abstract static class RPairListKeysNode extends Node {
+            @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
+
+            protected Object access(RPairList receiver) {
+                return listKeys(receiver, getNamesNode);
+            }
+        }
+
+        @Resolve(message = "KEY_INFO")
+        public abstract static class RPairListKeyInfoNode extends Node {
+            @Child private ListKeyInfoImplNode keyInfoNode = ListKeyInfoImplNodeGen.create();
+
+            protected Object access(VirtualFrame frame, TruffleObject receiver, Object idx) {
+                return keyInfoNode.execute(frame, receiver, idx);
+            }
+        }
+
+        @Resolve(message = "TO_NATIVE")
+        public abstract static class RPairListToNativeNode extends Node {
+            protected Object access(RPairList receiver) {
+                return toNativePointer(receiver);
+            }
+        }
+
+        @CanResolve
+        public abstract static class RPairListCheck extends Node {
+            protected static boolean test(TruffleObject receiver) {
+                return receiver instanceof RPairList;
+            }
+        }
+    }
+
+    abstract static class ListReadImplNode extends Node {
+        @Child private ExtractVectorNode extract;
+        @Child private R2Foreign r2Foreign;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        protected abstract Object execute(VirtualFrame frame, TruffleObject receiver, Object idx);
+
+        @Specialization
+        protected Object read(VirtualFrame frame, TruffleObject receiver, int idx,
+                        @Cached("createKeyInfoNode()") ListKeyInfoImplNode keyInfo) {
+            int info = keyInfo.execute(frame, receiver, idx);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + idx);
+            }
+            initExtractNode();
+            // idx + 1 R is indexing from 1
+            Object value = extract.apply(frame, receiver, new Object[]{idx + 1}, RLogical.valueOf(false), RMissing.instance);
+            initR2ForeignNode();
+            return r2Foreign.execute(value);
+        }
+
+        @Specialization
+        protected Object read(VirtualFrame frame, TruffleObject receiver, String field,
+                        @Cached("createKeyInfoNode()") ListKeyInfoImplNode keyInfo) {
+            // reading by an unknown name returns null,
+            // reading by an unknown index returns subscript out of bounds;
+            // let's be consistent at this place, the name should be known to the caller anyway
+            int info = keyInfo.execute(frame, receiver, field);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + field);
+            }
+            initExtractNode();
+            Object value = extract.applyAccessField(frame, receiver, field);
+            initR2ForeignNode();
+            return r2Foreign.execute(value);
+        }
+
+        @Fallback
+        protected Object read(VirtualFrame frame, TruffleObject receiver, Object field) {
+            throw UnknownIdentifierException.raise("" + field);
+        }
+
+        protected ListKeyInfoImplNode createKeyInfoNode() {
+            return ListKeyInfoImplNodeGen.create();
+        }
+
+        private void initR2ForeignNode() {
+            if (r2Foreign == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                r2Foreign = insert(R2ForeignNodeGen.create());
+            }
+        }
+
+        private void initExtractNode() {
+            if (extract == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                extract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+        }
+    }
+
+    abstract static class ListWriteImplNode extends Node {
+        @Child private ReplaceVectorNode replace;
+        @Child private Foreign2R foreign2R;
+
+        protected abstract Object execute(VirtualFrame frame, TruffleObject receiver, Object identifier, Object valueObj);
+
+        @Specialization
+        protected Object write(VirtualFrame frame, TruffleObject receiver, int idx, Object valueObj) {
+            // idx + 1 R is indexing from 1
+            return write(frame, receiver, new Object[]{idx + 1}, valueObj);
+        }
+
+        @Specialization
+        protected Object write(VirtualFrame frame, TruffleObject receiver, String field, Object valueObj) {
+            return write(frame, receiver, new Object[]{field}, valueObj);
+        }
+
+        private Object write(VirtualFrame frame, TruffleObject receiver, Object[] positions, Object valueObj) {
+            if (foreign2R == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                foreign2R = insert(Foreign2RNodeGen.create());
+            }
+            Object value = foreign2R.execute(valueObj);
+            if (replace == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                replace = insert(ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+            return replace.apply(frame, receiver, positions, value);
+        }
+
+        @Fallback
+        protected Object write(VirtualFrame frame, TruffleObject receiver, Object field, Object object) {
+            throw UnknownIdentifierException.raise("" + field);
+        }
+    }
+
+    abstract static class ListKeyInfoImplNode extends Node {
+        @Child private ExtractVectorNode extractNode;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract int execute(VirtualFrame frame, TruffleObject receiver, Object idx);
+
+        @Specialization
+        protected int keyInfo(VirtualFrame frame, TruffleObject receiver, int idx,
+                        @Cached("createLengthNode()") RLengthNode lenghtNode) {
+            return keyInfo(frame, receiver, (double) idx, lenghtNode);
+        }
+
+        @Specialization
+        protected int keyInfo(VirtualFrame frame, TruffleObject receiver, double idx,
+                        @Cached("createLengthNode()") RLengthNode lengthNode) {
+
+            int length = lengthNode.executeInteger(frame, receiver);
+            if (unknownIdentifier.profile(idx < 0 || idx >= length)) {
+                return 0;
+            }
+            initExtractNode();
+            return buildKeys(extractNode.apply(frame, receiver, new Object[]{idx + 1}, RLogical.valueOf(false), RMissing.instance));
+        }
+
+        @Specialization
+        protected int keyInfo(VirtualFrame frame, TruffleObject receiver, String identifier,
+                        @Cached("createNamesNode()") GetNamesAttributeNode namesNode) {
+            RStringVector names = namesNode.getNames(receiver);
+            boolean exists = false;
+            for (int i = 0; i < names.getLength(); i++) {
+                if (identifier.equals(names.getDataAt(i))) {
+                    exists = true;
+                    break;
+                }
+            }
+            if (unknownIdentifier.profile(!exists)) {
+                return 0;
+            }
+            initExtractNode();
+            return buildKeys(extractNode.applyAccessField(frame, receiver, identifier));
+        }
+
+        protected RLengthNode createLengthNode() {
+            return RLengthNode.create();
+        }
+
+        protected GetNamesAttributeNode createNamesNode() {
+            return GetNamesAttributeNode.create();
+        }
+
+        private int buildKeys(Object value) {
+            Builder builder = KeyInfo.newBuilder();
+            builder.setReadable(true).setWritable(true).setInvocable(value instanceof RFunction);
+            return builder.build();
+        }
+
+        private void initExtractNode() {
+            if (extractNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                extractNode = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+        }
+
+        @Fallback
+        protected int access(VirtualFrame frame, TruffleObject receiver, Object field) {
+            return 0;
+        }
+    }
+
+    private static boolean isBoxed(@SuppressWarnings("unused") TruffleObject receiver) {
+        return false;
+    }
+
+    private static boolean isNull(@SuppressWarnings("unused") TruffleObject receiver) {
+        return false;
+    }
+
+    private static boolean hasSize(@SuppressWarnings("unused") TruffleObject receiver) {
+        return true;
+    }
+
+    private static Object getSize(VirtualFrame frame, TruffleObject receiver, RLengthNode lengthNode) {
+        return lengthNode.executeInteger(frame, receiver);
+    }
+
+    private static Object listKeys(TruffleObject receiver, GetNamesAttributeNode getNamesNode) {
+        RStringVector names = getNamesNode.getNames(receiver);
+        return names != null ? names : RNull.instance;
+    }
+
+    private static Object toNativePointer(RTruffleObject receiver) {
+        return new NativePointer(receiver);
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
index 8453af22c21ab96832e32b20759ab70f81d90a5f..250948d7ab3689d80b5a4533ed5029e61a303922 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
@@ -23,24 +23,40 @@
 package com.oracle.truffle.r.engine.interop;
 
 import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.ForeignAccess.Factory26;
+import com.oracle.truffle.api.interop.KeyInfo;
 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.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.engine.TruffleRLanguageImpl;
-import com.oracle.truffle.r.engine.interop.RAbstractVectorAccessFactoryFactory.VectorReadNodeGen;
+import com.oracle.truffle.r.engine.interop.RAbstractVectorAccessFactoryFactory.VectorKeyInfoImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RAbstractVectorAccessFactoryFactory.VectorKeyInfoRootNodeGen;
+import com.oracle.truffle.r.engine.interop.RAbstractVectorAccessFactoryFactory.VectorReadImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RAbstractVectorAccessFactoryFactory.VectorWriteImplNodeGen;
 import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
+import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.runtime.data.RLogical;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
+import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+import java.util.List;
 
 abstract class InteropRootNode extends RootNode {
     InteropRootNode() {
@@ -65,34 +81,151 @@ public final class RAbstractVectorAccessFactory implements Factory26 {
         }
     }
 
-    abstract static class VectorReadNode extends InteropRootNode {
+    abstract static class VectorReadImplNode extends InteropRootNode {
 
-        @CompilationFinal private boolean lengthAccess = false;
-        @Child private ExtractVectorNode extract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
-        @Child private RLengthNode lengthNode = RLengthNode.create();
+        @Child private ExtractVectorNode extract;
+        @Child private R2Foreign r2Foreign;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        @Override
+        public final Object execute(VirtualFrame frame) {
+            Object indentifier = ForeignAccess.getArguments(frame).get(0);
+            Object receiver = ForeignAccess.getReceiver(frame);
+            return execute(frame, receiver, indentifier);
+        }
+
+        protected abstract Object execute(VirtualFrame frame, Object reciever, Object indentifier);
+
+        @Specialization
+        protected Object readIndexed(VirtualFrame frame, Object receiver, int idx,
+                        @Cached("createKeyInfoNode()") VectorKeyInfoImplNode keyInfo) {
+            int info = keyInfo.execute(frame, receiver, idx);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + idx);
+            }
+            return read(frame, receiver, new Object[]{idx + 1});
+        }
+
+        @Specialization
+        protected Object readIndexed(VirtualFrame frame, Object receiver, long idx,
+                        @Cached("createKeyInfoNode()") VectorKeyInfoImplNode keyInfo) {
+            int info = keyInfo.execute(frame, receiver, idx);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + idx);
+            }
+            return read(frame, receiver, new Object[]{idx + 1});
+        }
+
+        private Object read(VirtualFrame frame, Object receiver, Object[] positions) {
+            if (extract == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                extract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+            Object value = extract.apply(frame, receiver, positions, RLogical.TRUE, RLogical.TRUE);
+            if (r2Foreign == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                r2Foreign = insert(R2ForeignNodeGen.create());
+            }
+            return r2Foreign.execute(value);
+        }
+
+        @Fallback
+        protected Object read(VirtualFrame frame, Object receiver, Object indentifier) {
+            throw UnknownIdentifierException.raise("" + indentifier);
+        }
+
+        protected VectorKeyInfoImplNode createKeyInfoNode() {
+            return VectorKeyInfoImplNodeGen.create();
+        }
+    }
+
+    abstract static class VectorWriteImplNode extends InteropRootNode {
+        @Child private ReplaceVectorNode replace;
+        @Child private Foreign2R foreign2R;
+
+        @Override
+        public final Object execute(VirtualFrame frame) {
+            List<Object> arguments = ForeignAccess.getArguments(frame);
+            Object receiver = ForeignAccess.getReceiver(frame);
+            return execute(frame, receiver, arguments.get(0), arguments.get(1));
+        }
+
+        protected abstract Object execute(VirtualFrame frame, Object receiver, Object identifier, Object valueObj);
+
+        @Specialization
+        protected Object write(VirtualFrame frame, TruffleObject receiver, int idx, Object valueObj) {
+            // idx + 1 R is indexing from 1
+            return write(frame, receiver, new Object[]{idx + 1}, valueObj);
+        }
+
+        @Specialization
+        protected Object write(VirtualFrame frame, TruffleObject receiver, long idx, Object valueObj) {
+            // idx + 1 R is indexing from 1
+            return write(frame, receiver, new Object[]{idx + 1}, valueObj);
+        }
+
+        private Object write(VirtualFrame frame, TruffleObject receiver, Object[] positions, Object valueObj) {
+            if (foreign2R == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                foreign2R = insert(Foreign2RNodeGen.create());
+            }
+            Object value = foreign2R.execute(valueObj);
+            if (replace == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                replace = insert(ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+            return replace.apply(frame, receiver, positions, value);
+        }
+
+        @Fallback
+        protected Object write(VirtualFrame frame, Object receiver, Object field, Object object) {
+            throw UnknownIdentifierException.raise("" + field);
+        }
+    }
+
+    abstract static class VectorKeyInfoRootNode extends InteropRootNode {
+        @Child VectorKeyInfoImplNode keyInfoNode = VectorKeyInfoImplNodeGen.create();
 
         @Override
         public final Object execute(VirtualFrame frame) {
-            Object label = ForeignAccess.getArguments(frame).get(0);
+            Object indentifier = ForeignAccess.getArguments(frame).get(0);
             Object receiver = ForeignAccess.getReceiver(frame);
-            return execute(frame, receiver, label);
+            return execute(frame, receiver, indentifier);
         }
 
-        protected abstract Object execute(VirtualFrame frame, Object reciever, Object label);
+        protected abstract int execute(VirtualFrame frame, Object reciever, Object indentifier);
 
         @Specialization
-        protected Object readIndexed(VirtualFrame frame, Object receiver, int label) {
-            return extract.apply(frame, receiver, new Object[]{label + 1}, RLogical.TRUE, RLogical.TRUE);
+        protected int keyInfo(VirtualFrame frame, Object receiver, Object indentifier) {
+            return keyInfoNode.execute(frame, receiver, indentifier);
         }
+    }
+
+    abstract static class VectorKeyInfoImplNode extends Node {
+        @Child private RLengthNode lengthNode = RLengthNode.create();
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        protected abstract int execute(VirtualFrame frame, Object reciever, Object indentifier);
 
         @Specialization
-        protected Object readIndexed(VirtualFrame frame, Object receiver, long label) {
-            return extract.apply(frame, receiver, new Object[]{label + 1}, RLogical.TRUE, RLogical.TRUE);
+        protected int keyInfo(VirtualFrame frame, Object receiver, int idx) {
+            return keyInfo(frame, receiver, (long) idx);
         }
 
         @Specialization
-        protected Object readProperty(VirtualFrame frame, Object receiver, String label) {
-            return extract.applyAccessField(frame, receiver, label);
+        protected int keyInfo(VirtualFrame frame, Object receiver, long idx) {
+            if (unknownIdentifier.profile(idx < 0 || idx >= lengthNode.executeInteger(frame, receiver))) {
+                return 0;
+            }
+            KeyInfo.Builder builder = KeyInfo.newBuilder();
+            builder.setReadable(true).setWritable(true);
+            return builder.build();
+        }
+
+        @Fallback
+        protected int keyInfo(VirtualFrame frame, Object receiver, Object label) {
+            return 0;
         }
     }
 
@@ -108,7 +241,12 @@ public final class RAbstractVectorAccessFactory implements Factory26 {
 
     @Override
     public CallTarget accessIsExecutable() {
-        return null;
+        return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
+            @Override
+            public Object execute(VirtualFrame frame) {
+                return false;
+            }
+        });
     }
 
     @Override
@@ -150,12 +288,12 @@ public final class RAbstractVectorAccessFactory implements Factory26 {
 
     @Override
     public CallTarget accessRead() {
-        return Truffle.getRuntime().createCallTarget(VectorReadNodeGen.create());
+        return Truffle.getRuntime().createCallTarget(VectorReadImplNodeGen.create());
     }
 
     @Override
     public CallTarget accessWrite() {
-        return null;
+        return Truffle.getRuntime().createCallTarget(VectorWriteImplNodeGen.create());
     }
 
     @Override
@@ -185,7 +323,7 @@ public final class RAbstractVectorAccessFactory implements Factory26 {
 
     @Override
     public CallTarget accessKeyInfo() {
-        return null;
+        return Truffle.getRuntime().createCallTarget(VectorKeyInfoRootNodeGen.create());
     }
 
     @Override
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RArgsValuesAndNamesMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RArgsValuesAndNamesMR.java
index c62c3b20335d81ad01044fb019e7a0433ef9c43f..ff96aa902a84ef0490a935e174bc89025075646f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RArgsValuesAndNamesMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RArgsValuesAndNamesMR.java
@@ -22,12 +22,25 @@
  */
 package com.oracle.truffle.r.engine.interop;
 
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.KeyInfo;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.engine.interop.RArgsValuesAndNamesMRFactory.RArgsValuesAndNamesKeyInfoImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RArgsValuesAndNamesMRFactory.RArgsValuesAndNamesReadImplNodeGen;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 
 @MessageResolution(receiverType = RArgsValuesAndNames.class)
 public class RArgsValuesAndNamesMR {
@@ -61,8 +74,26 @@ public class RArgsValuesAndNamesMR {
 
     @Resolve(message = "READ")
     public abstract static class RArgsValuesAndNamesReadNode extends Node {
-        protected Object access(RArgsValuesAndNames receiver, int index) {
-            return receiver.getArgument(index);
+        @Child RArgsValuesAndNamesReadImplNode readNode = RArgsValuesAndNamesReadImplNodeGen.create();
+
+        protected Object access(RArgsValuesAndNames receiver, Object identifier) {
+            return readNode.execute(receiver, identifier);
+        }
+    }
+
+    @Resolve(message = "KEYS")
+    public abstract static class RArgsValuesAndNamesKeysNode extends Node {
+        protected Object access(RArgsValuesAndNames receiver) {
+            return RDataFactory.createStringVector(receiver.getSignature().getNames(), RDataFactory.COMPLETE_VECTOR);
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RArgsValuesAndNamesKeyInfoNode extends Node {
+        @Child private RArgsValuesAndNamesKeyInfoImplNode keyInfoNode = RArgsValuesAndNamesKeyInfoImplNodeGen.create();
+
+        protected Object access(VirtualFrame frame, RArgsValuesAndNames receiver, Object obj) {
+            return keyInfoNode.execute(frame, receiver, obj);
         }
     }
 
@@ -73,4 +104,88 @@ public class RArgsValuesAndNamesMR {
             return receiver instanceof RArgsValuesAndNames;
         }
     }
+
+    abstract static class RArgsValuesAndNamesReadImplNode extends Node {
+        @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create();
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract Object execute(RArgsValuesAndNames receiver, Object identifier);
+
+        @Specialization
+        protected Object access(RArgsValuesAndNames receiver, int index) {
+            if (unknownIdentifier.profile(index < 0 || index >= receiver.getLength())) {
+                throw UnknownIdentifierException.raise("" + index);
+            }
+            Object value = receiver.getArgument(index);
+            return r2Foreign.execute(value);
+        }
+
+        @Specialization
+        protected Object access(RArgsValuesAndNames receiver, String identifier) {
+            ArgumentsSignature sig = receiver.getSignature();
+            String[] names = sig.getNames();
+            int idx = -1;
+            for (int i = 0; i < names.length; i++) {
+                if (names[i].equals(identifier)) {
+                    idx = i;
+                    break;
+                }
+            }
+            if (unknownIdentifier.profile(idx < 0)) {
+                throw UnknownIdentifierException.raise("" + identifier);
+            }
+
+            Object value = receiver.getArgument(idx);
+            return r2Foreign.execute(value);
+        }
+
+        @Fallback
+        protected Object access(RArgsValuesAndNames receiver, Object identifier) {
+            throw UnknownIdentifierException.raise("" + identifier);
+        }
+    }
+
+    abstract static class RArgsValuesAndNamesKeyInfoImplNode extends Node {
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract Object execute(VirtualFrame frame, RArgsValuesAndNames receiver, Object idx);
+
+        @Specialization
+        protected Object access(VirtualFrame frame, RArgsValuesAndNames receiver, int idx) {
+            if (unknownIdentifier.profile(idx < 0 || idx >= receiver.getLength())) {
+                return 0;
+            }
+            return createKeyInfo(receiver, idx);
+        }
+
+        @Specialization
+        protected Object access(VirtualFrame frame, RArgsValuesAndNames receiver, String identifier) {
+            ArgumentsSignature sig = receiver.getSignature();
+            String[] names = sig.getNames();
+            int idx = -1;
+            for (int i = 0; i < names.length; i++) {
+                if (names[i].equals(identifier)) {
+                    idx = i;
+                    break;
+                }
+            }
+            if (unknownIdentifier.profile(idx < 0)) {
+                return 0;
+            }
+
+            return createKeyInfo(receiver, idx);
+        }
+
+        private Object createKeyInfo(RArgsValuesAndNames receiver, int idx) {
+            KeyInfo.Builder builder = KeyInfo.newBuilder();
+            builder.setReadable(true).setInvocable(receiver.getArgument(idx) instanceof RFunction);
+            return builder.build();
+        }
+
+        @Fallback
+        protected Object access(VirtualFrame frame, RArgsValuesAndNames receiver, Object field) {
+            return 0;
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RConnectionMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RConnectionMR.java
index 992891c4e563339f39c186cd79e98e47c97e9cfb..0d364d7041d352aa363b02bde22df019039f5fd5 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RConnectionMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RConnectionMR.java
@@ -24,12 +24,42 @@ package com.oracle.truffle.r.engine.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 
 @MessageResolution(receiverType = RConnection.class)
 public class RConnectionMR {
+
+    @Resolve(message = "IS_BOXED")
+    public abstract static class RConnectionIsBoxedNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    public abstract static class RConnectionHasSizeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "IS_NULL")
+    public abstract static class RConnectionIsNullNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RConnectionKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RConnection extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RContextMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RContextMR.java
index 012f9f218142dc1f61bc59cddd5dcc3c76ee3d3e..67d3201bba8025c38568cbd99b25073aab8c346d 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RContextMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RContextMR.java
@@ -24,12 +24,42 @@ package com.oracle.truffle.r.engine.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.context.RContext;
 
 @MessageResolution(receiverType = RContext.class)
 public class RContextMR {
+
+    @Resolve(message = "IS_BOXED")
+    public abstract static class RContextIsBoxedNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    public abstract static class RContextHasSizeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "IS_NULL")
+    public abstract static class RContextIsNullNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RContextKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RContext extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RDoubleMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RDoubleMR.java
index 6deaaf78d26dc2a016a0b473875678ca63f474b0..a31821ee84c8304b6eeca61eeb4ec853b8db7313 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RDoubleMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RDoubleMR.java
@@ -59,6 +59,13 @@ public class RDoubleMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RDoubleKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RDoubleCheck extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REmptyMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REmptyMR.java
index 13497b553c6fdadbd9f2bbbee94adeeee267b694..6537ae621cede6fffa8aa3ae882caf957c59dd1f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REmptyMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REmptyMR.java
@@ -53,6 +53,13 @@ public class REmptyMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class REmptyKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class REmptyCheck extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java
index 66a2aa3b62a0a1c36e58b0aa749e7634591b1743..7f57be4a7640a9904f58b1b003663248052ef927 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java
@@ -22,21 +22,36 @@
  */
 package com.oracle.truffle.r.engine.interop;
 
-import static com.oracle.truffle.r.engine.interop.Utils.javaToRPrimitive;
-
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.KeyInfo;
 import com.oracle.truffle.api.interop.KeyInfo.Builder;
+import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.engine.interop.REnvironmentMRFactory.REnvironmentKeyInfoImplNodeGen;
+import com.oracle.truffle.r.engine.interop.REnvironmentMRFactory.REnvironmentReadImplNodeGen;
+import com.oracle.truffle.r.engine.interop.REnvironmentMRFactory.REnvironmentWriteImplNodeGen;
 import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
+import com.oracle.truffle.r.runtime.data.RDouble;
+import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
+import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 
 @MessageResolution(receiverType = REnvironment.class)
 public class REnvironmentMR {
@@ -58,7 +73,7 @@ public class REnvironmentMR {
     @Resolve(message = "HAS_SIZE")
     public abstract static class REnvironmentHasSizeNode extends Node {
         protected Object access(@SuppressWarnings("unused") REnvironment receiver) {
-            return true;
+            return false;
         }
     }
 
@@ -71,20 +86,19 @@ public class REnvironmentMR {
 
     @Resolve(message = "READ")
     public abstract static class REnvironmentReadNode extends Node {
-        @Child private ExtractVectorNode extract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
+        @Child private REnvironmentReadImplNode readNode = REnvironmentReadImplNodeGen.create();
 
-        protected Object access(VirtualFrame frame, REnvironment receiver, String field) {
-            return extract.applyAccessField(frame, receiver, field);
+        protected Object access(VirtualFrame frame, REnvironment receiver, Object identifier) {
+            return readNode.execute(frame, receiver, identifier);
         }
     }
 
     @Resolve(message = "WRITE")
     public abstract static class REnvironmentWriteNode extends Node {
-        @Child private ReplaceVectorNode extract = ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
+        @Child private REnvironmentWriteImplNode writeNode = REnvironmentWriteImplNodeGen.create();
 
-        protected Object access(VirtualFrame frame, REnvironment receiver, String field, Object valueObj) {
-            Object value = javaToRPrimitive(valueObj);
-            return extract.apply(frame, receiver, new Object[]{field}, value);
+        protected Object access(VirtualFrame frame, REnvironment receiver, Object field, Object valueObj) {
+            return writeNode.execute(frame, receiver, field, valueObj);
         }
     }
 
@@ -98,8 +112,116 @@ public class REnvironmentMR {
 
     @Resolve(message = "KEY_INFO")
     public abstract static class REnvironmentKeyInfoNode extends Node {
+        @Child private REnvironmentKeyInfoImplNode keyInfoNode = REnvironmentKeyInfoImplNodeGen.create();
+
+        protected Object access(REnvironment receiver, Object obj) {
+            return keyInfoNode.execute(receiver, obj);
+        }
+    }
+
+    @CanResolve
+    public abstract static class REnvironmentCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof REnvironment;
+        }
+    }
+
+    abstract static class REnvironmentReadImplNode extends Node {
+        @Child private ExtractVectorNode extract;
+        @Child private R2Foreign r2Foreign;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        protected abstract Object execute(VirtualFrame frame, TruffleObject receiver, Object identifier);
+
+        @Specialization
+        protected Object access(VirtualFrame frame, REnvironment receiver, String identifier,
+                        @Cached("createKeyInfoNode()") REnvironmentKeyInfoImplNode keyInfo) {
+            int info = keyInfo.execute(receiver, identifier);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + identifier);
+            }
+
+            initExtractNode();
+            Object value = extract.applyAccessField(frame, receiver, identifier);
+            initR2ForeignNode();
+            return r2Foreign.execute(value);
+        }
+
+        @Fallback
+        protected Object access(VirtualFrame frame, TruffleObject receiver, Object identifier) {
+            throw UnknownIdentifierException.raise("" + identifier);
+        }
+
+        protected REnvironmentKeyInfoImplNode createKeyInfoNode() {
+            return REnvironmentKeyInfoImplNodeGen.create();
+        }
+
+        private void initR2ForeignNode() {
+            if (r2Foreign == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                r2Foreign = insert(R2ForeignNodeGen.create());
+            }
+        }
 
-        protected Object access(REnvironment receiver, String identifier) {
+        private void initExtractNode() {
+            if (extract == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                extract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+        }
+    }
+
+    abstract static class REnvironmentWriteImplNode extends Node {
+        @Child private Foreign2R foreign2R;
+        @Child private ReplaceVectorNode replace;
+
+        private final ConditionProfile roIdentifier = ConditionProfile.createBinaryProfile();
+
+        protected abstract Object execute(VirtualFrame frame, TruffleObject receiver, Object identifier, Object valueObj);
+
+        @Specialization
+        protected Object access(VirtualFrame frame, REnvironment receiver, String identifier, Object valueObj,
+                        @Cached("createKeyInfoNode()") REnvironmentKeyInfoImplNode keyInfo) {
+
+            int info = keyInfo.execute(receiver, identifier);
+            if (KeyInfo.isExisting(info)) {
+                if (roIdentifier.profile(!KeyInfo.isWritable(info))) {
+                    // TODO - this is a bit weird - should be Message.WRITE and identifier
+                    throw UnsupportedMessageException.raise(Message.WRITE);
+                }
+            } else if (receiver.isLocked()) {
+                throw UnsupportedMessageException.raise(Message.WRITE);
+            }
+            if (foreign2R == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                foreign2R = insert(Foreign2RNodeGen.create());
+            }
+            Object value = foreign2R.execute(valueObj);
+            if (replace == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                replace = insert(ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+            return replace.apply(frame, receiver, new Object[]{identifier}, value);
+        }
+
+        @Fallback
+        protected Object access(VirtualFrame frame, TruffleObject receiver, Object identifier, Object valueObj) {
+            throw UnknownIdentifierException.raise("" + identifier);
+        }
+
+        protected REnvironmentKeyInfoImplNode createKeyInfoNode() {
+            return REnvironmentKeyInfoImplNodeGen.create();
+        }
+    }
+
+    abstract static class REnvironmentKeyInfoImplNode extends Node {
+
+        protected abstract int execute(REnvironment receiver, Object identifier);
+
+        @Specialization
+        protected int access(REnvironment receiver, String identifier) {
             Object val = receiver.get(identifier);
             if (val == null) {
                 return 0;
@@ -109,15 +231,13 @@ public class REnvironmentMR {
             if (!receiver.isLocked() && !receiver.bindingIsLocked(identifier)) {
                 builder.setWritable(true);
             }
+            builder.setInvocable(val instanceof RFunction);
             return builder.build();
         }
-    }
-
-    @CanResolve
-    public abstract static class REnvironmentCheck extends Node {
 
-        protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof REnvironment;
+        @Fallback
+        protected int access(REnvironment receiver, Object identifier) {
+            return 0;
         }
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java
index 2ccac47016908c9d99126ad1307aedc1c368c1e5..d180a32a9046410c7cd08f22b4ab105d8de64c18 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RExternalPtrMR.java
@@ -24,12 +24,41 @@ package com.oracle.truffle.r.engine.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 
 @MessageResolution(receiverType = RExternalPtr.class)
 public class RExternalPtrMR {
+    @Resolve(message = "IS_BOXED")
+    public abstract static class RExternalPtrIsBoxedNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    public abstract static class RExternalPtrHasSizeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "IS_NULL")
+    public abstract static class RExternalPtrIsNullNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RExternalPtrKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RExternalPtrCheck extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
index 6b21deed0d9c5348e19b5c75349a48434e000216..3fd10f6ef50db12c670ffa5c10e750ecd2d7ffd8 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
@@ -47,6 +47,7 @@ import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.data.RUnboundValue;
+import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.ActiveBinding;
@@ -108,8 +109,8 @@ public final class RForeignAccessFactoryImpl implements RForeignAccessFactory {
             return RMissingMRForeign.ACCESS;
         } else if (obj instanceof REmpty) {
             return REmptyMRForeign.ACCESS;
-        } else if (obj instanceof RAbstractVector) {
-            return ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory());
+        } else if (obj instanceof RAbstractAtomicVector) {
+            return ForeignAccess.create(RAbstractAtomicVector.class, new RAbstractVectorAccessFactory());
         } else {
             ForeignAccess access = FFI_RForeignAccessFactoryImpl.getForeignAccess(obj);
             if (access != null) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
index 027ad9d9dd1f50fd87fa712acfdc1dc20166c2e7..0d6afa91c24e34b9813494e73606d156be01086c 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionMR.java
@@ -41,9 +41,42 @@ import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
+import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 
 @MessageResolution(receiverType = RFunction.class)
 public class RFunctionMR {
+
+    @Resolve(message = "IS_BOXED")
+    public abstract static class RFunctionIsBoxedNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    public abstract static class RFunctionHasSizeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "IS_NULL")
+    public abstract static class RFunctionIsNullNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RFunctionKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @Resolve(message = "IS_EXECUTABLE")
     public abstract static class RFunctionIsExecutabledNode extends Node {
         protected Object access(@SuppressWarnings("unused") RFunction receiver) {
@@ -60,6 +93,9 @@ public class RFunctionMR {
 
     @Resolve(message = "EXECUTE")
     public abstract static class RFunctionExecuteNode extends Node {
+        @Child private Foreign2R foreign2R = Foreign2RNodeGen.create();
+        @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create();
+
         private static final FrameDescriptor emptyFrameDescriptor = FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<interop>", new FrameDescriptor("R interop frame"));
         private static final RFrameSlot argsIdentifier = RFrameSlot.createTemp(false);
         private static final FrameSlot slot = FrameSlotChangeMonitor.findOrAddFrameSlot(emptyFrameDescriptor, argsIdentifier, FrameSlotKind.Object);
@@ -74,10 +110,15 @@ public class RFunctionMR {
             Object[] dummyFrameArgs = RArguments.createUnitialized();
             VirtualFrame dummyFrame = Truffle.getRuntime().createVirtualFrame(dummyFrameArgs, emptyFrameDescriptor);
 
-            RArgsValuesAndNames actualArgs = new RArgsValuesAndNames(arguments, ArgumentsSignature.empty(arguments.length));
+            Object[] convertedArguments = new Object[arguments.length];
+            for (int i = 0; i < arguments.length; i++) {
+                convertedArguments[i] = foreign2R.execute(arguments[i]);
+            }
+            RArgsValuesAndNames actualArgs = new RArgsValuesAndNames(convertedArguments, ArgumentsSignature.empty(arguments.length));
             try {
                 FrameSlotChangeMonitor.setObject(dummyFrame, slot, actualArgs);
-                return call.execute(dummyFrame, receiver);
+                Object value = call.execute(dummyFrame, receiver);
+                return r2Foreign.execute(value);
             } finally {
                 FrameSlotChangeMonitor.setObject(dummyFrame, slot, null);
             }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RIntegerMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RIntegerMR.java
index edd489b79917ad82d76521e1a2eada4d65b44077..498b95efac7e6914ba5a96755a01cc495a5fa7a2 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RIntegerMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RIntegerMR.java
@@ -52,9 +52,16 @@ public class RIntegerMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RIntegerKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @Resolve(message = "UNBOX")
     public abstract static class RIntegerUnboxNode extends Node {
-        protected double access(RInteger receiver) {
+        protected int access(RInteger receiver) {
             return receiver.getValue();
         }
     }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropScalarMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropScalarMR.java
index 0ae18bbe9de7a97cf20fc6b8b4e97198d3c8b119..798ab8277caded8b647a63aa5af2377f3e0c41c0 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropScalarMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropScalarMR.java
@@ -76,6 +76,13 @@ public class RInteropScalarMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RInteropScalarKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RInteropScalarCheck extends Node {
         protected static boolean test(TruffleObject receiver) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RLanguageMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RLanguageMR.java
index c0c338cfb7ec3716e457b23fe35a561d69d11684..353ac3daebfe9b9c1a4e8c4699a7b05cee0c4682 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RLanguageMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RLanguageMR.java
@@ -22,20 +22,27 @@
  */
 package com.oracle.truffle.r.engine.interop;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
-import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.KeyInfo;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RLogical;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 
 @MessageResolution(receiverType = RLanguage.class)
 public class RLanguageMR {
@@ -54,6 +61,13 @@ public class RLanguageMR {
         }
     }
 
+    @Resolve(message = "GET_SIZE")
+    public abstract static class RLanguageGetSizeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") RLanguage receiver) {
+            return receiver.getLength();
+        }
+    }
+
     @Resolve(message = "IS_NULL")
     public abstract static class RLanguageIsNullNode extends Node {
         protected Object access(@SuppressWarnings("unused") RLanguage receiver) {
@@ -70,44 +84,93 @@ public class RLanguageMR {
 
     @Resolve(message = "READ")
     public abstract static class RLanguageReadNode extends Node {
-        @Child private ExtractVectorNode extract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
+        @Child private ReadNode readNode = RLanguageMRFactory.ReadNodeGen.create();
 
-        protected Object access(VirtualFrame frame, RLanguage receiver, int label) {
-            return extract.apply(frame, receiver, new Object[]{label + 1}, RLogical.TRUE, RLogical.TRUE);
+        protected Object access(VirtualFrame frame, RLanguage receiver, Object identifier) {
+            return readNode.execute(frame, receiver, identifier);
         }
     }
 
-    @Resolve(message = "WRITE")
-    public abstract static class RLanguageWriteNode extends Node {
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RLanguageNode extends Node {
+        @Node.Child private KeyInfoNode keyInfoNode = RLanguageMRFactory.KeyInfoNodeGen.create();
 
-        @SuppressWarnings("unused")
-        protected Object access(RLanguage receiver, int label, Object valueObj) {
-            throw UnsupportedMessageException.raise(Message.WRITE);
+        protected Object access(VirtualFrame frame, RLanguage receiver, Object obj) {
+            return keyInfoNode.execute(receiver, obj);
         }
     }
 
-    @Resolve(message = "KEYS")
-    public abstract static class RLanguageKeysNode extends Node {
+    @CanResolve
+    public abstract static class RLanguageCheck extends Node {
 
-        protected Object access(@SuppressWarnings("unused") RLanguage receiver) {
-            return RNull.instance;
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof RLanguage;
         }
     }
 
-    @Resolve(message = "KEY_INFO")
-    public abstract static class RLanguageKeyInfoNode extends Node {
+    abstract static class ReadNode extends Node {
+        @Child private ExtractVectorNode extract;
+        @Child private R2Foreign r2Foreign;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract Object execute(VirtualFrame frame, RLanguage receiver, Object identifier);
+
+        @Specialization
+        protected Object access(VirtualFrame frame, RLanguage receiver, int idx,
+                        @Cached("createKeyInfoNode()") KeyInfoNode keyInfo) {
+
+            int info = keyInfo.execute(receiver, idx);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + idx);
+            }
+
+            if (extract == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                extract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
+            }
+            Object value = extract.apply(frame, receiver, new Object[]{idx + 1}, RLogical.TRUE, RLogical.TRUE);
+            if (value == null) {
+                return RNull.instance;
+            }
+
+            if (r2Foreign == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                r2Foreign = insert(R2ForeignNodeGen.create());
+            }
+            return r2Foreign.execute(value);
+        }
 
-        @SuppressWarnings("unused")
-        protected Object access(VirtualFrame frame, RLanguage receiver, String identifier) {
-            return 0;
+        @Fallback
+        protected Object access(VirtualFrame frame, RLanguage receiver, Object identifier) {
+            throw UnknownIdentifierException.raise("" + identifier);
+        }
+
+        protected static KeyInfoNode createKeyInfoNode() {
+            return RLanguageMRFactory.KeyInfoNodeGen.create();
         }
     }
 
-    @CanResolve
-    public abstract static class RLanguageCheck extends Node {
+    abstract static class KeyInfoNode extends Node {
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
 
-        protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof RLanguage;
+        abstract int execute(RLanguage receiver, Object identifier);
+
+        @Specialization
+        protected int access(RLanguage receiver, int idx) {
+            if (unknownIdentifier.profile(idx < 0 || idx >= receiver.getLength())) {
+                return 0;
+            }
+
+            KeyInfo.Builder builder = KeyInfo.newBuilder();
+            builder.setReadable(true);
+            // TODO what about writeble/invocable/...
+            return builder.build();
+        }
+
+        @Fallback
+        protected int access(RLanguage receiver, Object identifier) {
+            return 0;
         }
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
deleted file mode 100644
index 2501226929587783cc413eba72957ac64508654d..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2016, 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.engine.interop;
-
-import static com.oracle.truffle.r.engine.interop.Utils.javaToRPrimitive;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.CanResolve;
-import com.oracle.truffle.api.interop.KeyInfo;
-import com.oracle.truffle.api.interop.KeyInfo.Builder;
-import com.oracle.truffle.api.interop.MessageResolution;
-import com.oracle.truffle.api.interop.Resolve;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
-import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
-import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
-import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
-import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-
-@MessageResolution(receiverType = RList.class)
-public class RListMR {
-
-    @Resolve(message = "IS_BOXED")
-    public abstract static class RListIsBoxedNode extends Node {
-        protected Object access(@SuppressWarnings("unused") RList receiver) {
-            return false;
-        }
-    }
-
-    @Resolve(message = "HAS_SIZE")
-    public abstract static class RListHasSizeNode extends Node {
-        protected Object access(@SuppressWarnings("unused") RList receiver) {
-            return true;
-        }
-    }
-
-    @Resolve(message = "IS_NULL")
-    public abstract static class RListIsNullNode extends Node {
-        protected Object access(@SuppressWarnings("unused") RList receiver) {
-            return false;
-        }
-    }
-
-    @Resolve(message = "READ")
-    public abstract static class RListReadNode extends Node {
-        @Child private ExtractVectorNode extract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
-
-        protected Object access(VirtualFrame frame, RList receiver, String field) {
-            return extract.applyAccessField(frame, receiver, field);
-        }
-    }
-
-    @Resolve(message = "WRITE")
-    public abstract static class RListWriteNode extends Node {
-        @Child private ReplaceVectorNode replace = ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
-
-        protected Object access(VirtualFrame frame, RList receiver, String field, Object valueObj) {
-            Object value = javaToRPrimitive(valueObj);
-            return replace.apply(frame, receiver, new Object[]{field}, value);
-        }
-    }
-
-    @Resolve(message = "KEYS")
-    public abstract static class RListKeysNode extends Node {
-        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
-
-        protected Object access(RList receiver) {
-            RStringVector names = getNamesNode.getNames(receiver);
-            return names != null ? names : RNull.instance;
-        }
-    }
-
-    @Resolve(message = "KEY_INFO")
-    public abstract static class RListKeyInfoNode extends Node {
-        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
-        @Child private ExtractVectorNode extractNode;
-
-        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
-
-        protected Object access(VirtualFrame frame, RList receiver, String identifier) {
-            RStringVector names = getNamesNode.getNames(receiver);
-            boolean exists = false;
-            for (int i = 0; i < names.getLength(); i++) {
-                if (identifier.equals(names.getDataAt(i))) {
-                    exists = true;
-                    break;
-                }
-            }
-            if (unknownIdentifier.profile(!exists)) {
-                return 0;
-            }
-
-            Builder builder = KeyInfo.newBuilder();
-            builder.setReadable(true).setWritable(true);
-            if (extractNode == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                extractNode = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true));
-            }
-            Object value = extractNode.applyAccessField(frame, receiver, identifier);
-            builder.setInvocable(value instanceof RFunction);
-            return builder.build();
-        }
-    }
-
-    @Resolve(message = "TO_NATIVE")
-    public abstract static class RListToNativeNode extends Node {
-        protected Object access(RList receiver) {
-            return new NativePointer(receiver);
-        }
-    }
-
-    @CanResolve
-    public abstract static class RListCheck extends Node {
-
-        protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof RList;
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RMissingMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RMissingMR.java
index 6b776976b902216cd2c335d642e651c217408eea..22ed9437334387647a0af54e855179b0ed06aec1 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RMissingMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RMissingMR.java
@@ -53,6 +53,13 @@ public class RMissingMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RMissingKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RMissingCheck extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java
index dcbe27c3bcbc55cbe103d4d101747c795c957817..92aa246996cb3100f2eae689e52df2536ee8fb27 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java
@@ -58,6 +58,13 @@ public class RNullMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RNullKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @Resolve(message = "TO_NATIVE")
     public abstract static class RNullToNativeNode extends Node {
         protected Object access(@SuppressWarnings("unused") RNull receiver) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java
deleted file mode 100644
index aed400fc084ff2f0029eaaf462b8d79bf48b96d1..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2016, 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.engine.interop;
-
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.CanResolve;
-import com.oracle.truffle.api.interop.MessageResolution;
-import com.oracle.truffle.api.interop.Resolve;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
-import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
-import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
-import com.oracle.truffle.r.nodes.control.RLengthNode;
-import com.oracle.truffle.r.runtime.data.RLogical;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RPairList;
-
-@MessageResolution(receiverType = RPairList.class)
-public class RPairListMR {
-    @Resolve(message = "IS_BOXED")
-    public abstract static class RPairListIsBoxedNode extends Node {
-        protected Object access(@SuppressWarnings("unused") RPairList receiver) {
-            return false;
-        }
-    }
-
-    @Resolve(message = "HAS_SIZE")
-    public abstract static class RPairListHasSizeNode extends Node {
-        protected Object access(@SuppressWarnings("unused") RPairList receiver) {
-            return true;
-        }
-    }
-
-    @Resolve(message = "IS_NULL")
-    public abstract static class RPairListIsNullNode extends Node {
-        protected Object access(@SuppressWarnings("unused") RPairList receiver) {
-            return false;
-        }
-    }
-
-    @Resolve(message = "GET_SIZE")
-    public abstract static class RVectorGetSizeNode extends Node {
-        @Child private RLengthNode lengthNode = RLengthNode.create();
-
-        protected Object access(VirtualFrame frame, RPairList receiver) {
-            return lengthNode.executeInteger(frame, receiver);
-        }
-    }
-
-    @Resolve(message = "READ")
-    public abstract static class RPairListReadNode extends Node {
-        @Child private ExtractVectorNode extract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
-
-        protected Object access(VirtualFrame frame, RPairList receiver, Integer index) {
-            return extract.apply(frame, receiver, new Object[]{index}, RLogical.valueOf(false), RMissing.instance);
-        }
-    }
-
-    @Resolve(message = "TO_NATIVE")
-    public abstract static class RPairListToNativeNode extends Node {
-        protected Object access(RPairList receiver) {
-            return new NativePointer(receiver);
-        }
-    }
-
-    @CanResolve
-    public abstract static class RPairListCheck extends Node {
-
-        protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof RPairList;
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPromiseMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPromiseMR.java
index fc12c99c4c2c594a6c37042e634ebb678d18e814..16c7115a4dcb2497a2fb2e90c8b6c95a668c1f31 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPromiseMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPromiseMR.java
@@ -23,6 +23,8 @@
 package com.oracle.truffle.r.engine.interop;
 
 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.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.KeyInfo;
 import com.oracle.truffle.api.interop.MessageResolution;
@@ -31,6 +33,9 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.UnknownIdentifierException;
 import com.oracle.truffle.api.interop.UnsupportedTypeException;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.engine.interop.RPromiseMRFactory.RPromiseKeyInfoImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RPromiseMRFactory.RPromiseReadImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RPromiseMRFactory.RPromiseWriteImplNodeGen;
 import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -61,31 +66,62 @@ public class RPromiseMR {
 
     @Resolve(message = "READ")
     public abstract static class RPromiseReadNode extends Node {
+        @Child RPromiseReadImplNode readNode = RPromiseReadImplNodeGen.create();
 
         @TruffleBoundary
         protected Object access(RPromise receiver, String field) {
-            if (PROP_EXPR.equals(field)) {
-                return RDataFactory.createLanguage(receiver.getRep());
-            }
-            if (PROP_IS_EVALUATED.equals(field)) {
-                return RRuntime.asLogical(receiver.isEvaluated());
-            }
-            if (PROP_VALUE.equals(field)) {
-                // only read value if evaluated
-                if (receiver.isEvaluated()) {
-                    return receiver.getValue();
-                }
-                return RNull.instance;
-            }
-            throw UnknownIdentifierException.raise(field);
+            return readNode.execute(receiver, receiver);
         }
     }
 
     @Resolve(message = "WRITE")
     public abstract static class RPromiseWriteNode extends Node {
+        @Child RPromiseWriteImplNode writeNode = RPromiseWriteImplNodeGen.create();
 
         protected Object access(RPromise receiver, String field, Object valueObj) {
-            if (PROP_IS_EVALUATED.equals(field)) {
+            return writeNode.execute(receiver, valueObj, valueObj);
+        }
+    }
+
+    @Resolve(message = "KEYS")
+    public abstract static class RPromiseKeysNode extends Node {
+
+        protected Object access(@SuppressWarnings("unused") RPromise receiver) {
+            return RDataFactory.createStringVector(new String[]{PROP_VALUE, PROP_IS_EVALUATED, PROP_EXPR}, true);
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RPromiseKeyInfoNode extends Node {
+        @Child RPromiseKeyInfoImplNode keyInfoNode = RPromiseKeyInfoImplNodeGen.create();
+
+        protected Object access(RPromise receiver, String identifier) {
+            return keyInfoNode.execute(receiver, identifier);
+        }
+    }
+
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class RPromiseToNativeNode extends Node {
+        protected Object access(RPromise receiver) {
+            return new NativePointer(receiver);
+        }
+    }
+
+    @CanResolve
+    public abstract static class RPromiseCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof RPromise;
+        }
+    }
+
+    abstract static class RPromiseWriteImplNode extends Node {
+
+        protected abstract Object execute(RPromise receiver, Object identifier, Object valueObj);
+
+        @Specialization
+        protected Object access(RPromise receiver, String identifier, Object valueObj) {
+            if (PROP_IS_EVALUATED.equals(identifier)) {
                 if (!(valueObj instanceof Boolean)) {
                     throw UnsupportedTypeException.raise(new Object[]{valueObj});
                 }
@@ -98,21 +134,48 @@ public class RPromiseMR {
                 }
                 return RRuntime.asLogical(receiver.isEvaluated());
             }
-            throw UnknownIdentifierException.raise(field);
+            throw UnknownIdentifierException.raise(identifier);
+        }
+
+        @Fallback
+        protected Object access(RPromise receiver, Object identifier, Object valueObj) {
+            throw UnknownIdentifierException.raise("" + identifier);
         }
     }
 
-    @Resolve(message = "KEYS")
-    public abstract static class RPromiseKeysNode extends Node {
+    abstract static class RPromiseReadImplNode extends Node {
 
-        protected Object access(@SuppressWarnings("unused") RPromise receiver) {
-            return RDataFactory.createStringVector(new String[]{PROP_VALUE, PROP_IS_EVALUATED, PROP_EXPR}, true);
+        protected abstract Object execute(RPromise receiver, Object identifier);
+
+        @Specialization
+        protected Object access(@SuppressWarnings("unused") RPromise receiver, String identifier) {
+            if (PROP_EXPR.equals(identifier)) {
+                return RDataFactory.createLanguage(receiver.getRep());
+            }
+            if (PROP_IS_EVALUATED.equals(identifier)) {
+                return RRuntime.asLogical(receiver.isEvaluated());
+            }
+            if (PROP_VALUE.equals(identifier)) {
+                // only read value if evaluated
+                if (receiver.isEvaluated()) {
+                    return receiver.getValue();
+                }
+                return RNull.instance;
+            }
+            throw UnknownIdentifierException.raise(identifier);
+        }
+
+        @Fallback
+        protected Object access(RPromise receiver, Object identifier) {
+            throw UnknownIdentifierException.raise("" + identifier);
         }
     }
 
-    @Resolve(message = "KEY_INFO")
-    public abstract static class RPromiseKeyInfoNode extends Node {
+    abstract static class RPromiseKeyInfoImplNode extends Node {
+
+        protected abstract Object execute(RPromise receiver, Object identifier);
 
+        @Specialization
         protected Object access(@SuppressWarnings("unused") RPromise receiver, String identifier) {
             if (PROP_EXPR.equals(identifier) || PROP_VALUE.equals(identifier)) {
                 return KeyInfo.newBuilder().setReadable(true).build();
@@ -122,20 +185,10 @@ public class RPromiseMR {
                 return 0;
             }
         }
-    }
-
-    @Resolve(message = "TO_NATIVE")
-    public abstract static class RPromiseToNativeNode extends Node {
-        protected Object access(RPromise receiver) {
-            return new NativePointer(receiver);
-        }
-    }
 
-    @CanResolve
-    public abstract static class RPromiseCheck extends Node {
-
-        protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof RPromise;
+        @Fallback
+        protected Object access(RPromise receiver, Object identifier) {
+            return 0;
         }
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RS4ObjectMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RS4ObjectMR.java
index 084b6d390d0d88da68bb959c763d3f235c81782d..0f7c7346fd652a5d815418533c056444f1983215 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RS4ObjectMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RS4ObjectMR.java
@@ -22,17 +22,38 @@
  */
 package com.oracle.truffle.r.engine.interop;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
+import com.oracle.truffle.api.interop.KeyInfo;
+import com.oracle.truffle.api.interop.KeyInfo.Builder;
+import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.engine.interop.RS4ObjectMRFactory.RS4ObjectKeyInfoImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RS4ObjectMRFactory.RS4ObjectReadImplNodeGen;
+import com.oracle.truffle.r.engine.interop.RS4ObjectMRFactory.RS4ObjectWriteImplNodeGen;
 import com.oracle.truffle.r.nodes.attributes.ArrayAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.GetAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RS4Object;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
+import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 
 @MessageResolution(receiverType = RS4Object.class)
 public class RS4ObjectMR {
@@ -60,21 +81,19 @@ public class RS4ObjectMR {
 
     @Resolve(message = "READ")
     public abstract static class RS4ObjectReadNode extends Node {
-        @Child private GetAttributeNode getAttributeNode = GetAttributeNode.create();
+        @Child private RS4ObjectReadImplNode readNode = RS4ObjectReadImplNodeGen.create();
 
-        protected Object access(RS4Object receiver, String field) {
-            return getAttributeNode.execute(receiver, field);
+        protected Object access(RS4Object receiver, Object identifier) {
+            return readNode.execute(receiver, identifier);
         }
     }
 
     @Resolve(message = "WRITE")
     public abstract static class RS4ObjectWriteNode extends Node {
-        @Child private SetAttributeNode setAttributeNode = SetAttributeNode.create();
+        @Child private RS4ObjectWriteImplNode writeNode = RS4ObjectWriteImplNodeGen.create();
 
-        protected Object access(RS4Object receiver, String field, Object valueObj) {
-            Object value = Utils.javaToRPrimitive(valueObj);
-            setAttributeNode.execute(receiver, field, value);
-            return value;
+        protected Object access(RS4Object receiver, Object identifier, Object valueObj) {
+            return writeNode.execute(receiver, identifier, valueObj);
         }
     }
 
@@ -83,13 +102,26 @@ public class RS4ObjectMR {
         @Child private ArrayAttributeNode arrayAttrAccess = ArrayAttributeNode.create();
 
         protected Object access(RS4Object receiver) {
-            RAttribute[] attributes = arrayAttrAccess.execute(receiver.getAttributes());
-            String[] data = new String[attributes.length];
-            for (int i = 0; i < data.length; i++) {
-                data[i] = attributes[i].getName();
-            }
-            return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR);
+            return getKeys(receiver, arrayAttrAccess);
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RS4ObjectNode extends Node {
+        @Node.Child private RS4ObjectKeyInfoImplNode keyInfoNode = RS4ObjectKeyInfoImplNodeGen.create();
+
+        protected Object access(VirtualFrame frame, RS4Object receiver, Object obj) {
+            return keyInfoNode.execute(receiver, obj);
+        }
+    }
+
+    private static RAbstractStringVector getKeys(RS4Object s4, ArrayAttributeNode arrayAttrAccess) {
+        RAttribute[] attributes = arrayAttrAccess.execute(s4.getAttributes());
+        String[] data = new String[attributes.length];
+        for (int i = 0; i < data.length; i++) {
+            data[i] = attributes[i].getName();
         }
+        return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR);
     }
 
     @CanResolve
@@ -98,4 +130,138 @@ public class RS4ObjectMR {
             return receiver instanceof RS4Object;
         }
     }
+
+    abstract static class RS4ObjectReadImplNode extends Node {
+        @Child private GetAttributeNode getAttributeNode;
+        @Child private R2Foreign r2Foreign;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract Object execute(RS4Object receiver, Object identifier);
+
+        @Specialization
+        protected Object access(RS4Object receiver, String identifier,
+                        @Cached("createKeyInfoNode()") RS4ObjectKeyInfoImplNode keyInfo) {
+            int info = keyInfo.execute(receiver, identifier);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + identifier);
+            }
+
+            if (getAttributeNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getAttributeNode = insert(GetAttributeNode.create());
+            }
+            Object value = getAttributeNode.execute(receiver, identifier);
+            if (value == null) {
+                return RNull.instance;
+            }
+
+            if (r2Foreign == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                r2Foreign = insert(R2ForeignNodeGen.create());
+            }
+            return r2Foreign.execute(value);
+        }
+
+        @Fallback
+        protected Object access(RS4Object receiver, Object identifier) {
+            throw UnknownIdentifierException.raise("" + identifier);
+        }
+
+        protected static ArrayAttributeNode createArrayAttributeNode() {
+            return ArrayAttributeNode.create();
+        }
+
+        protected static RS4ObjectKeyInfoImplNode createKeyInfoNode() {
+            return RS4ObjectKeyInfoImplNodeGen.create();
+        }
+    }
+
+    abstract static class RS4ObjectWriteImplNode extends Node {
+        @Child private SetAttributeNode setAttributeNode;
+        @Child private Foreign2R foreign2R;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract Object execute(RS4Object receiver, Object identifier, Object valueObj);
+
+        @Specialization
+        protected Object access(RS4Object receiver, String identifier, Object valueObj,
+                        @Cached("createKeyInfoNode()") RS4ObjectKeyInfoImplNode keyInfo) {
+            int info = keyInfo.execute(receiver, identifier);
+            if (unknownIdentifier.profile(!KeyInfo.isExisting(info))) {
+                throw UnknownIdentifierException.raise("" + identifier);
+            }
+
+            if (!KeyInfo.isWritable(info)) {
+                throw UnsupportedMessageException.raise(Message.WRITE);
+            }
+
+            if (foreign2R == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                foreign2R = insert(Foreign2RNodeGen.create());
+            }
+            Object value = foreign2R.execute(valueObj);
+            if (setAttributeNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setAttributeNode = insert(SetAttributeNode.create());
+            }
+            setAttributeNode.execute(receiver, identifier, value);
+            return valueObj;
+        }
+
+        @Fallback
+        protected Object access(RS4Object receiver, Object identifier, Object valueObj) {
+            throw UnknownIdentifierException.raise("" + identifier);
+        }
+
+        protected static RS4ObjectKeyInfoImplNode createKeyInfoNode() {
+            return RS4ObjectKeyInfoImplNodeGen.create();
+        }
+    }
+
+    abstract static class RS4ObjectKeyInfoImplNode extends Node {
+        @Child private GetAttributeNode getAttributeNode;
+
+        private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile();
+
+        abstract int execute(RS4Object receiver, Object idx);
+
+        @Specialization
+        protected int access(RS4Object receiver, String identifier,
+                        @Cached("createArrayAttributeNode()") ArrayAttributeNode arrayAttrAccess) {
+            int idx = getAttrIndex(receiver, identifier, arrayAttrAccess);
+            if (unknownIdentifier.profile(idx < 0)) {
+                return 0;
+            }
+
+            if (getAttributeNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getAttributeNode = insert(GetAttributeNode.create());
+            }
+            Builder builder = KeyInfo.newBuilder();
+            builder.setReadable(true).setWritable(!identifier.equals("class"));
+            builder.setInvocable(getAttributeNode.execute(receiver, identifier) instanceof RFunction);
+            return builder.build();
+        }
+
+        protected static ArrayAttributeNode createArrayAttributeNode() {
+            return ArrayAttributeNode.create();
+        }
+
+        @Fallback
+        protected int access(RS4Object receiver, Object field) {
+            return 0;
+        }
+    }
+
+    private static int getAttrIndex(RS4Object receiver, String identifier, ArrayAttributeNode arrayAttrAccess) {
+        RAttribute[] attributes = arrayAttrAccess.execute(receiver.getAttributes());
+        for (int i = 0; i < attributes.length; i++) {
+            if (attributes[i].getName().equals(identifier)) {
+                return i;
+            }
+        }
+        return -1;
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java
index 3c00139f868ba1148f9f7d281eb70956feb519d9..f175339f0953f8a87e4ba5f9d50c0c4568119a77 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RSymbolMR.java
@@ -33,6 +33,34 @@ import com.oracle.truffle.r.runtime.data.RSymbol;
 @MessageResolution(receiverType = RSymbol.class)
 public class RSymbolMR {
 
+    @Resolve(message = "IS_BOXED")
+    public abstract static class RSymbolIsBoxedNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    public abstract static class RSymbolHasSizeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "IS_NULL")
+    public abstract static class RSymbolIsNullNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver) {
+            return false;
+        }
+    }
+
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RSymbolKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @Resolve(message = "TO_NATIVE")
     public abstract static class RSymbolToNativeNode extends Node {
         protected Object access(RSymbol receiver) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RUnboundValueMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RUnboundValueMR.java
index f8c1e15d8bf90e750ba2049d64eae63b10f2fea0..c2d60fa3a6da3b023cd43db7b6e8b83e34d10a37 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RUnboundValueMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RUnboundValueMR.java
@@ -52,6 +52,13 @@ public class RUnboundValueMR {
         }
     }
 
+    @Resolve(message = "KEY_INFO")
+    public abstract static class RUnboundValueKeyInfoNode extends Node {
+        protected Object access(@SuppressWarnings("unused") TruffleObject receiver, @SuppressWarnings("unused") Object identifier) {
+            return 0;
+        }
+    }
+
     @CanResolve
     public abstract static class RUnboundValueCheck extends Node {
 
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/AbstractMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/AbstractMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c53520bccbbf856cdd5ae2f7cfaabcf039f6a44f
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/AbstractMRTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Assert;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+abstract class AbstractMRTest {
+
+    /**
+     * Create TruffleObject-s to be rudimentary tested for IS_NULL, IS_BOXED/UNBOX, IS_EXECUTABLE,
+     * IS_POINTER, HAS_SIZE/GET_SIZE/KEYS behavior.
+     * 
+     * @return
+     * @throws Exception
+     */
+    protected abstract TruffleObject[] createTruffleObjects() throws Exception;
+
+    /**
+     * Create a test TruffleObject having size == 0.
+     */
+    protected abstract TruffleObject createEmptyTruffleObject() throws Exception;
+
+    protected String[] getKeys() {
+        return null;
+    }
+
+    protected boolean isNull(TruffleObject obj) {
+        return false;
+    }
+
+    protected boolean isExecutable(TruffleObject obj) {
+        return false;
+    }
+
+    protected boolean isPointer(TruffleObject obj) {
+        return false;
+    }
+
+    protected boolean isBoxed(TruffleObject obj) {
+        return false;
+    }
+
+    protected boolean hasSize(TruffleObject obj) {
+        return false;
+    }
+
+    protected int getSize(TruffleObject obj) {
+        throw new UnsupportedOperationException("override if hasSize returns true");
+    }
+
+    protected Object getUnboxed(TruffleObject obj) {
+        throw new UnsupportedOperationException("override if isBoxed returns true");
+    }
+
+    @Test
+    public void testIsNull() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            assertEquals(isNull(obj), ForeignAccess.sendIsNull(Message.IS_NULL.createNode(), obj));
+        }
+    }
+
+    @Test
+    public void testIsExecutable() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            assertEquals(isExecutable(obj), ForeignAccess.sendIsExecutable(Message.IS_EXECUTABLE.createNode(), obj));
+        }
+    }
+
+    @Test
+    public void testIsPointer() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            assertEquals(isPointer(obj), ForeignAccess.sendIsPointer(Message.IS_POINTER.createNode(), obj));
+        }
+    }
+
+    @Test
+    public void testNativePointer() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            try {
+                assertTrue(ForeignAccess.sendToNative(Message.TO_NATIVE.createNode(), obj) instanceof NativePointer);
+            } catch (UnsupportedMessageException unsupportedMessageException) {
+            }
+        }
+    }
+
+    @Test
+    public void testSize() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            boolean hasSize = ForeignAccess.sendHasSize(Message.HAS_SIZE.createNode(), obj);
+            assertEquals("" + obj.getClass() + " " + obj, hasSize(obj), hasSize);
+            if (hasSize) {
+                assertEquals(getSize(obj), ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), obj));
+            } else {
+                assertInteropException(() -> ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), obj), UnsupportedMessageException.class);
+            }
+        }
+    }
+
+    @Test
+    public void testBoxed() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            boolean isBoxed = ForeignAccess.sendIsBoxed(Message.IS_BOXED.createNode(), obj);
+            assertEquals(isBoxed(obj), isBoxed);
+            if (isBoxed) {
+                assertEquals(getUnboxed(obj), ForeignAccess.sendUnbox(Message.UNBOX.createNode(), obj));
+            }
+            // else {
+            // assertInteropException(() -> ForeignAccess.sendUnbox(Message.UNBOX.createNode(),
+            // obj), UnsupportedMessageException.class);
+            // }
+        }
+    }
+
+    @Test
+    public void testKeys() throws Exception {
+        String[] keys = getKeys();
+        if (keys == null) {
+            return;
+        }
+        for (TruffleObject obj : createTruffleObjects()) {
+            TruffleObject keysObj = ForeignAccess.sendKeys(Message.KEYS.createNode(), obj);
+            int size = (int) ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), keysObj);
+            assertEquals(keys.length, size);
+
+            Set<Object> set = new HashSet<>();
+            for (int i = 0; i < size; i++) {
+                set.add(ForeignAccess.sendRead(Message.READ.createNode(), keysObj, i));
+            }
+            for (String key : keys) {
+                assertTrue(set.contains(key));
+            }
+        }
+    }
+
+    @Test
+    public void testEmpty() throws Exception {
+
+        TruffleObject obj = createEmptyTruffleObject();
+        if (obj != null) {
+            if (hasSize(obj)) {
+                int size = (int) ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), obj);
+                assertEquals(0, size);
+            }
+
+            TruffleObject keys = null;
+            try {
+                keys = ForeignAccess.sendKeys(Message.KEYS.createNode(), obj);
+            } catch (UnsupportedMessageException ex) {
+            }
+            if (keys != null) {
+                boolean keysHasSize = ForeignAccess.sendHasSize(Message.HAS_SIZE.createNode(), keys);
+                if (keysHasSize) {
+                    int keysSize = (int) ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), keys);
+                    assertEquals(0, keysSize);
+                }
+            }
+        }
+    }
+
+    protected interface ForeignCall {
+        void call() throws Exception;
+    }
+
+    protected void assertInteropException(ForeignCall c, Class<? extends InteropException> expectedClazz) {
+        boolean ie = false;
+        try {
+            c.call();
+        } catch (InteropException ex) {
+            if (expectedClazz != null && ex.getClass() != expectedClazz) {
+                Assert.fail(expectedClazz + " was expected but got instead: " + ex);
+            }
+            ie = true;
+        } catch (Exception ex) {
+            if (expectedClazz != null && ex.getClass() != expectedClazz) {
+                Assert.fail(expectedClazz + " was expected but got instead: " + ex);
+            } else {
+                Assert.fail("InteropException was expected but got insteat: " + ex);
+            }
+        }
+        assertTrue(ie);
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/ActiveBindingMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/ActiveBindingMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d750d368ec1afa3abf1d5ea5b9c3385fa43bb27
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/ActiveBindingMRTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.env.frame.ActiveBinding;
+
+public class ActiveBindingMRTest extends AbstractMRTest {
+
+    @Override
+    protected boolean isBoxed(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected Object getUnboxed(TruffleObject obj) {
+        return ((ActiveBinding) obj).readValue();
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        Source src = Source.newBuilder("f=function() {}").mimeType("text/x-r").name("test.R").build();
+        PolyglotEngine.Value result = engine.eval(src);
+        RFunction fn = result.as(RFunction.class);
+        return new TruffleObject[]{new ActiveBinding(RType.Any, fn)};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/ListMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/ListMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9486402b6da24bc7fc821c33be2b9fc71edc3ffa
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/ListMRTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.KeyInfo;
+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.interop.UnsupportedTypeException;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ListMRTest extends AbstractMRTest {
+
+    private String testValues = "i=1L, d=2.1, b=TRUE, fn=function() {}, n=NULL, 4";
+    private final PolyglotEngine engine;
+
+    public ListMRTest() {
+        engine = PolyglotEngine.newBuilder().build();
+    }
+
+    @Test
+    public void testNativePointer() throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException {
+        assertTrue(ForeignAccess.sendToNative(Message.TO_NATIVE.createNode(), create("list", testValues)) instanceof NativePointer);
+        assertTrue(ForeignAccess.sendToNative(Message.TO_NATIVE.createNode(), create("pairlist", testValues)) instanceof NativePointer);
+    }
+
+    @Test
+    public void testKeysReadWrite() throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException {
+        testKeysReadWrite("list");
+        testKeysReadWrite("pairlist");
+    }
+
+    private void testKeysReadWrite(String createFun) throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException {
+
+        RAbstractContainer l = create(createFun, testValues);
+
+        assertEquals(1, ForeignAccess.sendRead(Message.READ.createNode(), l, "i"));
+        assertEquals(2.1, ForeignAccess.sendRead(Message.READ.createNode(), l, "d"));
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), l, "b"));
+        assertTrue(ForeignAccess.sendRead(Message.READ.createNode(), l, "n") instanceof RNull);
+
+        assertEquals(1, ForeignAccess.sendRead(Message.READ.createNode(), l, 0));
+        assertEquals(2.1, ForeignAccess.sendRead(Message.READ.createNode(), l, 1));
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), l, 2));
+        assertTrue(ForeignAccess.sendRead(Message.READ.createNode(), l, 4) instanceof RNull);
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), l, -1), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), l, 0f), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), l, 4d), UnknownIdentifierException.class);
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), l, "nnnoooonnne"), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), l, 100), UnknownIdentifierException.class);
+
+        TruffleObject obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), l, "d", 123.1);
+        assertEquals(123.1, ForeignAccess.sendRead(Message.READ.createNode(), obj, "d"));
+
+        obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), l, 2, false);
+        RAbstractContainer returnedList = JavaInterop.asJavaObject(RAbstractContainer.class, obj);
+        assertEquals((byte) 0, returnedList.getDataAtAsObject(2));
+        assertEquals(false, ForeignAccess.sendRead(Message.READ.createNode(), obj, "b"));
+
+        obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), l, "newnew", "nneeww");
+        assertEquals("nneeww", ForeignAccess.sendRead(Message.READ.createNode(), obj, "newnew"));
+
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), l, 0f, false), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), l, 0d, false), UnknownIdentifierException.class);
+    }
+
+    @Test
+    public void testKeysInfo() {
+        testKeysInfo("list");
+        testKeysInfo("pairlist");
+    }
+
+    public void testKeysInfo(String createFun) {
+
+        RAbstractContainer l = create(createFun, testValues);
+
+        int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "nnoonnee");
+        assertFalse(KeyInfo.isExisting(info));
+        assertFalse(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "d");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "fn");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertTrue(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, -1);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, l.getLength());
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, 1f);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, 0);
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, 1d);
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+    }
+
+    private RAbstractContainer create(String createFun, String values) {
+        Source src = Source.newBuilder(createFun + "(" + values + ")").mimeType("text/x-r").name("test.R").build();
+        PolyglotEngine.Value result = engine.eval(src);
+        return result.as(RAbstractContainer.class);
+    }
+
+    @Override
+    protected String[] getKeys() {
+        return new String[]{"i", "d", "b", "fn", "n", ""};
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{create("list", testValues), create("pairlist", testValues)};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        // cant have an emtpy pair list
+        return create("list", "");
+    }
+
+    @Override
+    protected boolean hasSize(TruffleObject arg0) {
+        return true;
+    }
+
+    @Override
+    protected int getSize(TruffleObject obj) {
+        return obj instanceof RList ? ((RList) obj).getLength() : ((RPairList) obj).getLength();
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RArgsValuesAndNamesMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RArgsValuesAndNamesMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7e3eb84811fe0017b4ef7af701aa29ecc300d4d
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RArgsValuesAndNamesMRTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.KeyInfo;
+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.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class RArgsValuesAndNamesMRTest extends AbstractMRTest {
+
+    @Test
+    public void testReadWriteIdx() throws Exception {
+        TruffleObject args = createTruffleObjects()[0];
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), args, 1f), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), args, 1d), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), args, -1), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), args, 100), UnknownIdentifierException.class);
+
+        assertEquals("abc", ForeignAccess.sendRead(Message.READ.createNode(), args, 0));
+        assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), args, 1));
+        assertEquals(1.1, ForeignAccess.sendRead(Message.READ.createNode(), args, 2));
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), args, 3));
+        assertTrue(ForeignAccess.sendRead(Message.READ.createNode(), args, 4) instanceof RFunction);
+        assertTrue(ForeignAccess.sendRead(Message.READ.createNode(), args, 5) instanceof RNull);
+
+        TruffleObject emptyargs = RArgsValuesAndNames.EMPTY;
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), emptyargs, 0), UnknownIdentifierException.class);
+
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), args, 0, 321), UnsupportedMessageException.class);
+    }
+
+    @Test
+    public void testReadWriteName() throws Exception {
+        TruffleObject args = createTruffleObjects()[0];
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), args, "nnnnooooonnnneee"), UnknownIdentifierException.class);
+
+        assertEquals("abc", ForeignAccess.sendRead(Message.READ.createNode(), args, "s"));
+        assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), args, "i"));
+        assertEquals(1.1, ForeignAccess.sendRead(Message.READ.createNode(), args, "d"));
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), args, "b"));
+        assertTrue(ForeignAccess.sendRead(Message.READ.createNode(), args, "fn") instanceof RFunction);
+        assertTrue(ForeignAccess.sendRead(Message.READ.createNode(), args, "n") instanceof RNull);
+
+        TruffleObject emptyargs = RArgsValuesAndNames.EMPTY;
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), emptyargs, "s"), UnknownIdentifierException.class);
+
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), args, "s", "aaa"), UnsupportedMessageException.class);
+    }
+
+    @Test
+    public void testKeysInfo() throws Exception {
+        TruffleObject e = createTruffleObjects()[0];
+        int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "nnoonnee");
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, 1f);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, 0);
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "s");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "b");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "fn");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertTrue(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "n");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+    }
+
+    private String[] names = {"s", "i", "d", "b", "fn", "n"};
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        Source src = Source.newBuilder("f=function() {}").mimeType("text/x-r").name("test.R").build();
+        PolyglotEngine.Value result = engine.eval(src);
+        RFunction fn = result.as(RFunction.class);
+        Object[] values = {"abc", 123, 1.1, RRuntime.asLogical(true), fn, RNull.instance};
+        return new TruffleObject[]{new RArgsValuesAndNames(values, ArgumentsSignature.get(names))};
+    }
+
+    @Override
+    protected String[] getKeys() {
+        return names;
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return RArgsValuesAndNames.EMPTY;
+    }
+
+    @Override
+    protected boolean hasSize(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected int getSize(TruffleObject obj) {
+        return ((RArgsValuesAndNames) obj).getLength();
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/Utils.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RDoubleMRTest.java
similarity index 50%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/Utils.java
rename to com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RDoubleMRTest.java
index 6d044539546c64e6d2b97ca0d994e6c2db852bc1..887f384fbb2b4b1c578934ca3717de357a6bf08f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/Utils.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RDoubleMRTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,26 +20,30 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.test.engine.interop;
 
-import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.data.RDouble;
 
-class Utils {
-    static Object javaToRPrimitive(Object valueObj) {
-        Object value = valueObj;
-        if (value instanceof Short) {
-            value = (int) ((Short) value).shortValue();
-        } else if (value instanceof Float) {
-            float floatValue = ((Float) value).floatValue();
-            value = new Double(floatValue);
-        } else if (value instanceof Boolean) {
-            boolean booleanValue = ((Boolean) value).booleanValue();
-            value = booleanValue ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
-        } else if (value instanceof Character) {
-            value = (int) ((Character) value).charValue();
-        } else if (value instanceof Byte) {
-            value = (int) ((Byte) value).byteValue();
-        }
-        return value;
+public class RDoubleMRTest extends AbstractMRTest {
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{RDouble.valueOf(1.1)};
+    }
+
+    @Override
+    protected boolean isBoxed(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected Object getUnboxed(TruffleObject obj) {
+        return ((RDouble) obj).getValue();
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REmptyMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REmptyMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ec6184661511c474fd2d3944a65e3e2254971c3
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REmptyMRTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.data.REmpty;
+
+public class REmptyMRTest extends AbstractMRTest {
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{REmpty.instance};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REnvironmentMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REnvironmentMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..33ff1c3fea8760a3e3a6ff1ae0e6ded6f60e33e2
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/REnvironmentMRTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.KeyInfo;
+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.interop.java.JavaInterop;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class REnvironmentMRTest extends AbstractMRTest {
+
+    @Test
+    public void testReadWrite() throws Exception {
+        REnvironment e = (REnvironment) createTruffleObjects()[0];
+
+        assertEquals("aaa", ForeignAccess.sendRead(Message.READ.createNode(), e, "s"));
+        assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), e, "i"));
+        assertEquals(123.1, ForeignAccess.sendRead(Message.READ.createNode(), e, "d"));
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), e, "b"));
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), e, "nnnoooonnne"), UnknownIdentifierException.class);
+
+        TruffleObject obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), e, "s", "abc");
+        Object value = ForeignAccess.sendRead(Message.READ.createNode(), obj, "s");
+        assertEquals("abc", value);
+
+        obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), e, "b", false);
+        REnvironment env = JavaInterop.asJavaObject(REnvironment.class, obj);
+        assertEquals((byte) 0, env.get("b"));
+        value = ForeignAccess.sendRead(Message.READ.createNode(), obj, "b");
+        assertEquals(false, value);
+
+        obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), e, "i", (short) 1234);
+        value = ForeignAccess.sendRead(Message.READ.createNode(), obj, "i");
+        assertTrue(value instanceof Integer);
+        assertEquals(1234, value);
+
+        obj = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), e, "newnew", "nneeww");
+        value = ForeignAccess.sendRead(Message.READ.createNode(), obj, "newnew");
+        assertEquals("nneeww", value);
+
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), e, "l", 667), UnsupportedMessageException.class);
+
+        e.lock(true);
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), e, "newnew2", "nneeww"), UnsupportedMessageException.class);
+        e.lock(false);
+
+        // can't read/write via index
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), e, 0), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), e, 1f), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), e, 1, 321), UnknownIdentifierException.class);
+    }
+
+    @Test
+    public void testKeysInfo() throws Exception {
+        TruffleObject e = createTruffleObjects()[0];
+        int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "nnoonnee");
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, 0);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, 1f);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "s");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "b");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "fn");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertTrue(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "n");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), e, "l");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+    }
+
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        Source src = Source.newBuilder("e <- new.env(); e$s <- 'aaa'; e$i <- 123L; e$d <- 123.1; e$b <- TRUE; e$fn <- function() {}; e$n <- NULL; e$l <- 666; lockBinding('l', e); e").mimeType(
+                        "text/x-r").name("test.R").build();
+        return new TruffleObject[]{engine.eval(src).as(REnvironment.class)};
+    }
+
+    @Override
+    protected String[] getKeys() {
+        return new String[]{"s", "i", "d", "b", "fn", "n", "l"};
+    }
+
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        Source src = Source.newBuilder("new.env()").mimeType("text/x-r").name("test.R").build();
+        return engine.eval(src).as(REnvironment.class);
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RFunctionMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RFunctionMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..03c5a908b7d16155168ccb025558a4f9e0e7eaa0
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RFunctionMRTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ArityException;
+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.interop.UnsupportedTypeException;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class RFunctionMRTest extends AbstractMRTest {
+
+    @Test
+    public void testExecute() throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
+        RFunction f = create("function() {}");
+        assertTrue(ForeignAccess.sendIsExecutable(Message.IS_EXECUTABLE.createNode(), f));
+
+        TruffleObject result = (TruffleObject) ForeignAccess.sendExecute(Message.createExecute(0).createNode(), f);
+        assertTrue(ForeignAccess.sendIsNull(Message.IS_NULL.createNode(), result));
+
+        f = create("function() {1L}");
+        assertEquals(1, ForeignAccess.sendExecute(Message.createExecute(0).createNode(), f));
+
+        f = create("function() {1}");
+        assertEquals(1.0, ForeignAccess.sendExecute(Message.createExecute(0).createNode(), f));
+
+        f = create("function() {TRUE}");
+        assertEquals(true, ForeignAccess.sendExecute(Message.createExecute(0).createNode(), f));
+
+        f = create("function(a) {a}");
+        assertEquals("abc", ForeignAccess.sendExecute(Message.createExecute(1).createNode(), f, "abc"));
+
+        f = create("function(a) { is.logical(a) }");
+        assertEquals(true, ForeignAccess.sendExecute(Message.createExecute(1).createNode(), f, true));
+
+        f = create("function(a) { as.external.short(a) }");
+        assertTrue(ForeignAccess.sendExecute(Message.createExecute(1).createNode(), f, 123) instanceof Short);
+
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() {
+        return new TruffleObject[]{create("function() {}")};
+    }
+
+    private RFunction create(String fun) {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        Source src = Source.newBuilder(fun).mimeType("text/x-r").name("test.R").build();
+        PolyglotEngine.Value result = engine.eval(src);
+        return result.as(RFunction.class);
+    }
+
+    @Override
+    protected boolean isExecutable(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RIntegerMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RIntegerMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b44b1e1505ac661845551f16366558ff23957003
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RIntegerMRTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.data.RInteger;
+
+public class RIntegerMRTest extends AbstractMRTest {
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{RInteger.valueOf(123)};
+    }
+
+    @Override
+    protected boolean isBoxed(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected Object getUnboxed(TruffleObject obj) {
+        return ((RInteger) obj).getValue();
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RInteropScalarMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RInteropScalarMRTest.java
index eba1e8a9b35af3772e1c0b914a182eeb7b10f7ee..fbbcd375cc9765f0ccca69862527eacc5a4cc9d0 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RInteropScalarMRTest.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RInteropScalarMRTest.java
@@ -31,36 +31,55 @@ import org.junit.Test;
 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.source.Source;
-import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.data.RInteropScalar;
+import org.junit.Assert;
 
-public class RInteropScalarMRTest {
+public class RInteropScalarMRTest extends AbstractMRTest {
 
     @Test
-    public void testRInteroptScalar() throws UnsupportedMessageException {
-        testRIS("toByte", "" + Byte.MAX_VALUE, Byte.class);
-        testRIS("toChar", "'a'", Character.class);
-        testRIS("toFloat", "" + Float.MAX_VALUE, Float.class);
-        testRIS("toLong", "" + Long.MAX_VALUE, Long.class);
-        testRIS("toShort", "" + Short.MAX_VALUE, Short.class);
+    public void testRInteroptScalar() throws Exception {
+        for (TruffleObject obj : createTruffleObjects()) {
+            RInteropScalar is = (RInteropScalar) obj;
+            testRIS(obj, is.getJavaType());
+        }
     }
 
-    private static void testRIS(String toInteropScalarBuiltin, String value, Class<?> unboxedType) throws UnsupportedMessageException {
-        TruffleObject l = createRInteroptScalarTO(toInteropScalarBuiltin, value);
+    private static void testRIS(TruffleObject obj, Class<?> unboxedType) throws Exception {
+        assertFalse(ForeignAccess.sendIsNull(Message.IS_NULL.createNode(), obj));
+        assertFalse(ForeignAccess.sendHasSize(Message.HAS_SIZE.createNode(), obj));
 
-        assertFalse(ForeignAccess.sendIsNull(Message.IS_NULL.createNode(), l));
-        assertFalse(ForeignAccess.sendHasSize(Message.HAS_SIZE.createNode(), l));
+        assertTrue(ForeignAccess.sendIsBoxed(Message.IS_BOXED.createNode(), obj));
+        Object ub = ForeignAccess.sendUnbox(Message.UNBOX.createNode(), obj);
+        assertEquals(unboxedType, ub.getClass().getField("TYPE").get(null));
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{RInteropScalar.RInteropByte.valueOf(Byte.MAX_VALUE),
+                        RInteropScalar.RInteropChar.valueOf('a'),
+                        RInteropScalar.RInteropFloat.valueOf(Float.MAX_VALUE),
+                        RInteropScalar.RInteropLong.valueOf(Long.MAX_VALUE),
+                        RInteropScalar.RInteropShort.valueOf(Short.MAX_VALUE)};
+    }
+
+    @Override
+    protected boolean isBoxed(TruffleObject arg0) {
+        return true;
+    }
 
-        assertTrue(ForeignAccess.sendIsBoxed(Message.IS_BOXED.createNode(), l));
-        Object ub = ForeignAccess.sendUnbox(Message.UNBOX.createNode(), l);
-        assertEquals(unboxedType, ub.getClass());
+    @Override
+    protected Object getUnboxed(TruffleObject obj) {
+        RInteropScalar is = (RInteropScalar) obj;
+        try {
+            return is.getClass().getDeclaredMethod("getValue").invoke(is);
+        } catch (Exception ex) {
+            Assert.fail("can't read interop scalar value " + ex);
+        }
+        return null;
     }
 
-    private static TruffleObject createRInteroptScalarTO(String toInteropScalarBuiltin, String value) {
-        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
-        Source src = Source.newBuilder(".fastr.interop." + toInteropScalarBuiltin + "(" + value + ")").mimeType("text/x-r").name("test.R").build();
-        PolyglotEngine.Value result = engine.eval(src);
-        return result.as(TruffleObject.class);
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RLanguageMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RLanguageMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..66a7eac2592032c1d8a85dc93d0ea12f381ec54f
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RLanguageMRTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.KeyInfo;
+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.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+import org.junit.Assert;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class RLanguageMRTest extends AbstractMRTest {
+
+    @Test
+    public void testKeysInfo() throws Exception {
+        RLanguage rl = (RLanguage) createTruffleObjects()[0];
+        int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), rl, "nnoonnee");
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), rl, 1f);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), rl, rl.getLength());
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), rl, 0);
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), rl, 1);
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), rl, 2);
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+    }
+
+    @Test
+    public void testReadWrite() throws Exception {
+        RLanguage rl = (RLanguage) createTruffleObjects()[0];
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), rl, "nnnoooonnne"), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), rl, rl.getLength()), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), rl, 0d), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), rl, 0f), UnknownIdentifierException.class);
+
+        // TODO add some meaningfull read tests
+        Assert.assertNotNull(ForeignAccess.sendRead(Message.READ.createNode(), rl, 0));
+
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), rl, "aaa", "abc"), UnsupportedMessageException.class);
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), rl, 0, "abc"), UnsupportedMessageException.class);
+
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        // TODO any simpler way to create a RLanguage ?
+        String srcTxt = "ne <- new.env(); delayedAssign('x', 1 + 2, assign.env = ne); substitute(x, ne)";
+        Source src = Source.newBuilder(srcTxt).mimeType("text/x-r").name("test.R").build();
+        PolyglotEngine.Value result = engine.eval(src);
+        return new TruffleObject[]{result.as(RLanguage.class)};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+
+    @Override
+    protected boolean hasSize(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected int getSize(TruffleObject obj) {
+        return ((RLanguage) obj).getLength();
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RListMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RListMRTest.java
deleted file mode 100644
index f083bd862435f28e05e067ce9ff667382bef0025..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RListMRTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.test.engine.interop;
-
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.KeyInfo;
-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.interop.UnsupportedTypeException;
-
-import org.junit.Test;
-
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.vm.PolyglotEngine;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class RListMRTest {
-
-    @Test
-    public void testKeysReadWrite() throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException {
-        TruffleObject l = createRListTruffleObject("n1=1L, n2=2L, 3, 4");
-
-        TruffleObject keys = ForeignAccess.sendKeys(Message.KEYS.createNode(), l);
-        assertEquals("n1", ForeignAccess.sendRead(Message.READ.createNode(), keys, 0));
-        assertEquals("n2", ForeignAccess.sendRead(Message.READ.createNode(), keys, 1));
-        assertEquals("", ForeignAccess.sendRead(Message.READ.createNode(), keys, 2));
-        assertEquals("", ForeignAccess.sendRead(Message.READ.createNode(), keys, 3));
-
-        assertEquals(4, ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), keys));
-        assertEquals(1, ForeignAccess.sendRead(Message.READ.createNode(), l, "n1"));
-        assertEquals(2, ForeignAccess.sendRead(Message.READ.createNode(), l, "n2"));
-
-        // TODO fails - should this return NULL as in l[""] or 3 as in l$""
-        // ElementAccessMode.FIELD_SUBSCRIPT vs .SUBSCRIPT doesn't seem to have any effect
-        // assertEquals(3, ForeignAccess.sendRead(Message.READ.createNode(), l, ""));
-
-        // TODO - more tests for NA, ... ?
-
-        l = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), l, "n2", 123);
-        assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), l, "n2"));
-    }
-
-    @Test
-    public void testKeysInfo() {
-        TruffleObject l = createRListTruffleObject("n1=1, n2=2");
-        int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "nnoonnee");
-        assertFalse(KeyInfo.isExisting(info));
-        assertFalse(KeyInfo.isReadable(info));
-        assertFalse(KeyInfo.isWritable(info));
-        assertFalse(KeyInfo.isInvocable(info));
-        assertFalse(KeyInfo.isInternal(info));
-
-        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "n2");
-        assertTrue(KeyInfo.isExisting(info));
-        assertTrue(KeyInfo.isReadable(info));
-        assertTrue(KeyInfo.isWritable(info));
-        assertFalse(KeyInfo.isInvocable(info));
-        assertFalse(KeyInfo.isInternal(info));
-
-        l = createRListTruffleObject("f=function() {}");
-        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "f");
-        assertTrue(KeyInfo.isExisting(info));
-        assertTrue(KeyInfo.isReadable(info));
-        assertTrue(KeyInfo.isWritable(info));
-        assertTrue(KeyInfo.isInvocable(info));
-        assertFalse(KeyInfo.isInternal(info));
-    }
-
-    private static TruffleObject createRListTruffleObject(String values) {
-        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
-        Source src = Source.newBuilder("list(" + values + ")").mimeType("text/x-r").name("test.R").build();
-        PolyglotEngine.Value result = engine.eval(src);
-        return result.as(TruffleObject.class);
-    }
-}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RMissingMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RMissingMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0a39b78a89784dc48f690c0547d928f831a693f
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RMissingMRTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.data.RMissing;
+
+public class RMissingMRTest extends AbstractMRTest {
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{RMissing.instance};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RNullMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RNullMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9c943090466aa6330051dcad113c8b61c947d45
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RNullMRTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public class RNullMRTest extends AbstractMRTest {
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{RNull.instance};
+    }
+
+    @Override
+    protected boolean isNull(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RS4ObjectMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RS4ObjectMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..233ec91a21f9c078cbf2c6bab016204bff283c1e
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RS4ObjectMRTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.KeyInfo;
+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.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.data.RS4Object;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class RS4ObjectMRTest extends AbstractMRTest {
+
+    public RS4ObjectMRTest() {
+    }
+
+    @Test
+    public void testKeysInfo() throws Exception {
+        TruffleObject s4 = createTruffleObjects()[0];
+        int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, "nnoonnee");
+        assertFalse(KeyInfo.isExisting(info));
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, 0);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, 1f);
+        assertFalse(KeyInfo.isExisting(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, "s");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, "b");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, "fn");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertTrue(KeyInfo.isWritable(info));
+        assertTrue(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+
+        info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), s4, "class");
+        assertTrue(KeyInfo.isExisting(info));
+        assertTrue(KeyInfo.isReadable(info));
+        assertFalse(KeyInfo.isWritable(info));
+        assertFalse(KeyInfo.isInvocable(info));
+        assertFalse(KeyInfo.isInternal(info));
+    }
+
+    @Test
+    public void testReadWrite() throws Exception {
+        TruffleObject s4 = createTruffleObjects()[0];
+
+        assertEquals("aaa", ForeignAccess.sendRead(Message.READ.createNode(), s4, "s"));
+        assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), s4, "i"));
+        assertEquals(1.1, ForeignAccess.sendRead(Message.READ.createNode(), s4, "d"));
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), s4, "b"));
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), s4, "nnnoooonnne"), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), s4, 0), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), s4, 1d), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), s4, 1f), UnknownIdentifierException.class);
+
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), s4, "class", "cantchangeclass"), UnsupportedMessageException.class);
+        // TODO this should fail !!!
+        // assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), s4, "i",
+        // "cant write string into int slot"), UnsupportedMessageException.class);
+
+        ForeignAccess.sendWrite(Message.WRITE.createNode(), s4, "s", "abc");
+        Object value = ForeignAccess.sendRead(Message.READ.createNode(), s4, "s");
+        assertEquals("abc", value);
+
+        ForeignAccess.sendWrite(Message.WRITE.createNode(), s4, "b", false);
+        value = ForeignAccess.sendRead(Message.READ.createNode(), s4, "b");
+        assertEquals(false, value);
+
+        ForeignAccess.sendWrite(Message.WRITE.createNode(), s4, "i", (short) 1234);
+        value = ForeignAccess.sendRead(Message.READ.createNode(), s4, "i");
+        assertTrue(value instanceof Integer);
+        assertEquals(1234, value);
+
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() {
+        PolyglotEngine engine = PolyglotEngine.newBuilder().build();
+        String srcTxt = "setClass('test', representation(s = 'character', d = 'numeric', i = 'integer', b = 'logical', fn = 'function'));" +
+                        "new('test', s = 'aaa', d = 1.1, i=123L, b = TRUE, fn = function() {})";
+        Source src = Source.newBuilder(srcTxt).mimeType("text/x-r").name("test.R").build();
+        PolyglotEngine.Value result = engine.eval(src);
+        RS4Object s4 = result.as(RS4Object.class);
+        return new TruffleObject[]{s4};
+    }
+
+    @Override
+    protected String[] getKeys() {
+        return new String[]{"s", "d", "i", "b", "fn", "class"};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RUboundValueMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RUboundValueMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..baa61ec385903879fc2583e334d6a9e19c63eeeb
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RUboundValueMRTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.data.RUnboundValue;
+
+public class RUboundValueMRTest extends AbstractMRTest {
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{RUnboundValue.instance};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return null;
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/VectorMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/VectorMRTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8547a34982335c092fbf7751da6a2775e20bde57
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/VectorMRTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.test.engine.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.KeyInfo;
+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.interop.java.JavaInterop;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+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;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class VectorMRTest extends AbstractMRTest {
+
+    private final PolyglotEngine engine;
+
+    public VectorMRTest() {
+        engine = PolyglotEngine.newBuilder().build();
+    }
+
+    @Test
+    public void testReadWrite() throws Exception {
+        final TruffleObject vi = create("1L:10L");
+        assertEquals(3, ForeignAccess.sendRead(Message.READ.createNode(), vi, 2));
+        assertEquals(3, ForeignAccess.sendRead(Message.READ.createNode(), vi, 2L));
+
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), vi, "a"), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendRead(Message.READ.createNode(), vi, 100), UnknownIdentifierException.class);
+        assertInteropException(() -> ForeignAccess.sendWrite(Message.WRITE.createNode(), vi, "s", "abc"), UnknownIdentifierException.class);
+
+        TruffleObject vd = create("1.1:10.1");
+        assertEquals(1.1, ForeignAccess.sendRead(Message.READ.createNode(), vd, 0));
+
+        TruffleObject vb = create("c(TRUE, FALSE, TRUE)");
+        assertEquals(true, ForeignAccess.sendRead(Message.READ.createNode(), vb, 0));
+
+        TruffleObject nvi = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), vi, 0, 123);
+        RAbstractIntVector returnedVec = JavaInterop.asJavaObject(RAbstractIntVector.class, nvi);
+        assertEquals(123, returnedVec.getDataAt(0));
+        assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), nvi, 0));
+
+        assertEquals(10, ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), nvi));
+        nvi = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), nvi, 100, 321);
+        assertEquals(101, ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), nvi));
+        assertEquals(321, ForeignAccess.sendRead(Message.READ.createNode(), nvi, 100));
+
+        nvi = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), nvi, 0, "abc");
+        RAbstractVector vec = JavaInterop.asJavaObject(RAbstractVector.class, nvi);
+        assertTrue(vec instanceof RAbstractStringVector);
+        assertEquals("abc", ForeignAccess.sendRead(Message.READ.createNode(), nvi, 0));
+
+    }
+
+    @Test
+    public void testKeyInfo() throws Exception {
+        TruffleObject v = create("c(TRUE, FALSE, TRUE)");
+        assertInteropException(() -> ForeignAccess.sendKeys(Message.KEYS.createNode(), v), UnsupportedMessageException.class);
+
+        int keyInfo = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), v, 0);
+        assertTrue(KeyInfo.isExisting(keyInfo));
+        assertTrue(KeyInfo.isReadable(keyInfo));
+        assertTrue(KeyInfo.isWritable(keyInfo));
+        assertFalse(KeyInfo.isInvocable(keyInfo));
+        assertFalse(KeyInfo.isInternal(keyInfo));
+
+        keyInfo = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), v, 100);
+        assertFalse(KeyInfo.isExisting(keyInfo));
+    }
+
+    @Override
+    protected TruffleObject[] createTruffleObjects() throws Exception {
+        return new TruffleObject[]{create("1:10"), create("as.numeric()")};
+    }
+
+    @Override
+    protected TruffleObject createEmptyTruffleObject() throws Exception {
+        return create("as.numeric()");
+    }
+
+    @Override
+    protected boolean isBoxed(TruffleObject obj) {
+        return ((RAbstractVector) obj).getLength() == 1;
+    }
+
+    @Override
+    protected boolean hasSize(TruffleObject obj) {
+        return true;
+    }
+
+    @Override
+    protected int getSize(TruffleObject obj) {
+        return ((RAbstractVector) obj).getLength();
+    }
+
+    private TruffleObject create(String createTxt) throws Exception {
+        Source src = Source.newBuilder(createTxt).mimeType("text/x-r").name("test.R").build();
+        return engine.eval(src).as(RAbstractVector.class);
+    }
+
+}
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index 6634237a98c549da9b3dc3ef7509bf14d2f51092..ef0a29387f7aa8f5dfc73e2d1bc0757c50587c15 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -313,7 +313,7 @@ def _test_subpackage(name):
     return '.'.join((_test_package(), name))
 
 def _simple_generated_unit_tests():
-    return map(_test_subpackage, ['engine.shell', 'library.base', 'library.grid', 'library.fastrGrid', 'library.methods', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'parser', 'rffi', 'rng', 'runtime.data', 'S4'])
+    return map(_test_subpackage, ['engine.shell', 'engine.interop', 'library.base', 'library.grid', 'library.fastrGrid', 'library.methods', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'parser', 'rffi', 'rng', 'runtime.data', 'S4'])
 
 def _simple_unit_tests():
     return _simple_generated_unit_tests() + [_test_subpackage('tck')]