diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
index a34050460abaabc7cd1cc2a5211859fb1cb193a7..23e1c21944ba4daa919b257b55b313535d562668 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
@@ -85,7 +85,6 @@ import com.oracle.truffle.r.runtime.data.RObject;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
-import com.oracle.truffle.r.runtime.data.RSequence;
 import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
@@ -638,16 +637,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     @Override
     @TruffleBoundary
     public Object Rf_duplicate(Object x, int deep) {
-        guarantee(x != null, "unexpected type: null instead of " + x.getClass().getSimpleName());
-        guarantee(x instanceof RShareable || x instanceof RSequence || x instanceof RExternalPtr,
-                        "unexpected type: " + x + " is " + x.getClass().getSimpleName() + " instead of RShareable or RExternalPtr");
-        if (x instanceof RShareable) {
-            return deep == 1 ? ((RShareable) x).deepCopy() : ((RShareable) x).copy();
-        } else if (x instanceof RSequence) {
-            return ((RSequence) x).materialize();
-        } else {
-            return ((RExternalPtr) x).copy();
-        }
+        throw implementedAsNode();
     }
 
     @Override
@@ -1770,4 +1760,14 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         throw implementedAsNode();
     }
 
+    @Override
+    public int R_has_slot(Object container, Object name) {
+        throw implementedAsNode();
+    }
+
+    @Override
+    public Object CLOENV(Object x) {
+        throw implementedAsNode();
+    }
+
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/DuplicateNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/DuplicateNodes.java
new file mode 100644
index 0000000000000000000000000000000000000000..928e657a66034de3763094c573e9c2a8e08793f5
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/DuplicateNodes.java
@@ -0,0 +1,46 @@
+package com.oracle.truffle.r.ffi.impl.nodes;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.ffi.impl.nodes.DuplicateNodesFactory.DuplicateNodeGen;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RSequence;
+import com.oracle.truffle.r.runtime.data.RShareable;
+import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+public final class DuplicateNodes {
+
+    public abstract static class DuplicateNode extends FFIUpCallNode.Arg2 {
+
+        @Specialization
+        public Object duplicateShareable(RShareable x, int deep) {
+            assert !isReusableForDuplicate(x);
+            return deep == 1 ? x.deepCopy() : x.copy();
+        }
+
+        @Specialization
+        public Object duplicateSequence(RSequence x, @SuppressWarnings("unused") int deep) {
+            return x.materialize();
+        }
+
+        @Specialization
+        public Object duplicateExternalPtr(RExternalPtr x, @SuppressWarnings("unused") int deep) {
+            return x.copy();
+        }
+
+        @Specialization(guards = "isReusableForDuplicate(val)")
+        public Object returnReusable(Object val, @SuppressWarnings("unused") int deep) {
+            return val;
+        }
+
+        protected static boolean isReusableForDuplicate(Object o) {
+            return o == RNull.instance || o instanceof REnvironment || o instanceof RSymbol;
+        }
+
+        public static DuplicateNode create() {
+            return DuplicateNodeGen.create();
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
index 515524a94b9a9918eb060ebca0e589d53b01d1ad..9fed0338aa1805925b3a7d6aebf42fdb98bcbfe6 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
@@ -23,24 +23,30 @@
 package com.oracle.truffle.r.ffi.impl.nodes;
 
 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.dsl.TypeSystemReference;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.GetFunctionEnvironmentNodeGen;
 import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.LENGTHNodeGen;
 import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RDoNewObjectNodeGen;
 import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RDoSlotAssignNodeGen;
 import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RDoSlotNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RHasSlotNodeGen;
 import com.oracle.truffle.r.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
+import com.oracle.truffle.r.nodes.access.HasSlotNode;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNode;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctionsFactory.SetNamesAttributeNodeGen;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.GetFunctionEnvironmentNode;
 import com.oracle.truffle.r.nodes.objects.NewObject;
 import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.CharSXPWrapper;
 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 com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypes;
@@ -184,6 +190,31 @@ public final class MiscNodes {
         }
     }
 
+    @TypeSystemReference(RTypes.class)
+    public abstract static class RHasSlotNode extends FFIUpCallNode.Arg2 {
+
+        @Child private HasSlotNode hasSlotNode;
+
+        RHasSlotNode() {
+            hasSlotNode = HasSlotNode.create(false);
+        }
+
+        @Specialization
+        Object doSlot(Object o, RSymbol nameSym) {
+            return hasSlotNode.executeAccess(o, nameSym.getName());
+        }
+
+        @Fallback
+        @SuppressWarnings("unused")
+        Object doSlot(Object o, Object name) {
+            return false;
+        }
+
+        public static RHasSlotNode create() {
+            return RHasSlotNodeGen.create();
+        }
+    }
+
     @TypeSystemReference(RTypes.class)
     public abstract static class NamesGetsNode extends FFIUpCallNode.Arg2 {
 
@@ -204,4 +235,21 @@ public final class MiscNodes {
         }
     }
 
+    @TypeSystemReference(RTypes.class)
+    public abstract static class GetFunctionEnvironment extends FFIUpCallNode.Arg1 {
+
+        /**
+         * Returns the environment that {@code func} was created in.
+         */
+        @Specialization
+        protected Object environment(RFunction fun,
+                        @Cached("create()") GetFunctionEnvironmentNode getEnvNode) {
+            return getEnvNode.getEnvironment(fun);
+        }
+
+        public static GetFunctionEnvironment create() {
+            return GetFunctionEnvironmentNodeGen.create();
+        }
+    }
+
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
index ff460ed40591f81dd108c10dc4633eb5a52eaa13..84f515f07604199b201654672e783c746d28d9d7 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
@@ -31,6 +31,7 @@ import com.oracle.truffle.r.ffi.impl.nodes.AttributesAccessNodes.CopyMostAttrib;
 import com.oracle.truffle.r.ffi.impl.nodes.AttributesAccessNodes.TAG;
 import com.oracle.truffle.r.ffi.impl.nodes.CoerceNodes.CoerceVectorNode;
 import com.oracle.truffle.r.ffi.impl.nodes.CoerceNodes.VectorToPairListNode;
+import com.oracle.truffle.r.ffi.impl.nodes.DuplicateNodes;
 import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CADDRNode;
 import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CADRNode;
 import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CARNode;
@@ -184,6 +185,7 @@ public interface StdUpCallsRFFI {
 
     int OBJECT(Object x);
 
+    @RFFIUpCallNode(DuplicateNodes.DuplicateNode.class)
     Object Rf_duplicate(Object x, int deep);
 
     long Rf_any_duplicated(Object x, int fromLast);
@@ -366,4 +368,10 @@ public interface StdUpCallsRFFI {
 
     @RFFIUpCallNode(MatchNodes.NonNullStringMatchNode.class)
     Object Rf_NonNullStringMatch(Object s, Object t);
+
+    @RFFIUpCallNode(MiscNodes.RHasSlotNode.class)
+    int R_has_slot(Object container, Object name);
+
+    @RFFIUpCallNode(MiscNodes.GetFunctionEnvironment.class)
+    Object CLOENV(Object x);
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
index bbdcd3fd61e16e8c2ca127b3bd2d7cffdd7f8355..768892a9ea92960e4fdfa2c9c80efcf6549abc37 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.library.methods;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.runtime.RError.Message.INVALID_LIST_FOR_SUBSTITUTION;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 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.r.nodes.RASTUtils;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.runtime.RError;
-import static com.oracle.truffle.r.runtime.RError.Message.INVALID_LIST_FOR_SUBSTITUTION;
 import com.oracle.truffle.r.runtime.RSubstitute;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
diff --git a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
index e48fc20e3ce43ba1249164d1c78ab328bd6df01d..948ff349e59d7cdce6e9cc17d9363f5747215e18 100644
--- a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
@@ -277,12 +277,10 @@ typedef double (*call_Rf_punif)(double a, double b, double c, int d, int e);
 typedef double (*call_Rf_runif)(double x, double y);
 typedef SEXP (*call_Rf_match)(SEXP itable, SEXP ix, int nmatch);
 typedef Rboolean (*call_Rf_NonNullStringMatch)(SEXP s, SEXP t);
-
 typedef SEXP (*call_getvar)();
-
 typedef SEXP (*call_R_ParseVector)(SEXP text, int n, SEXP srcFile);
-
 typedef SEXPTYPE (*call_Rf_str2type)(const char *s);
+typedef SEXP (*call_CLOENV)(SEXP closure);
 
 // connections
 
diff --git a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
index a86b1d5693b84d22db836eb98851b7078a19216d..681477a7f7c25591a930c9fb26bf42b3762040a3 100644
--- a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
@@ -8,165 +8,167 @@
 #define CAR_x 3
 #define CDDR_x 4
 #define CDR_x 5
-#define COMPLEX_x 6
-#define DUPLICATE_ATTRIB_x 7
-#define ENCLOS_x 8
-#define FASTR_getConnectionChar_x 9
-#define GetRNGstate_x 10
-#define INTEGER_x 11
-#define IS_S4_OBJECT_x 12
-#define LENGTH_x 13
-#define LOGICAL_x 14
-#define NAMED_x 15
-#define OBJECT_x 16
-#define PRCODE_x 17
-#define PRENV_x 18
-#define PRINTNAME_x 19
-#define PRSEEN_x 20
-#define PRVALUE_x 21
-#define PutRNGstate_x 22
-#define RAW_x 23
-#define RDEBUG_x 24
-#define REAL_x 25
-#define RSTEP_x 26
-#define R_BaseEnv_x 27
-#define R_BaseNamespace_x 28
-#define R_BindingIsLocked_x 29
-#define R_CHAR_x 30
-#define R_CleanUp_x 31
-#define R_ExternalPtrAddr_x 32
-#define R_ExternalPtrProtected_x 33
-#define R_ExternalPtrTag_x 34
-#define R_FindNamespace_x 35
-#define R_GetConnection_x 36
-#define R_GlobalContext_x 37
-#define R_GlobalEnv_x 38
-#define R_Home_x 39
-#define R_HomeDir_x 40
-#define R_Interactive_x 41
-#define R_MakeExternalPtr_x 42
-#define R_MethodsNamespace_x 43
-#define R_NamespaceRegistry_x 44
-#define R_NewHashedEnv_x 45
-#define R_ParseVector_x 46
-#define R_PreserveObject_x 47
-#define R_PromiseExpr_x 48
-#define R_ProtectWithIndex_x 49
-#define R_ReadConnection_x 50
-#define R_ReleaseObject_x 51
-#define R_Reprotect_x 52
-#define R_SetExternalPtrAddr_x 53
-#define R_SetExternalPtrProtected_x 54
-#define R_SetExternalPtrTag_x 55
-#define R_TempDir_x 56
-#define R_ToplevelExec_x 57
-#define R_WriteConnection_x 58
-#define R_alloc_x 59
-#define R_compute_identical_x 60
-#define R_do_MAKE_CLASS_x 61
-#define R_do_new_object_x 62
-#define R_do_slot_x 63
-#define R_do_slot_assign_x 64
-#define R_getClassDef_x 65
-#define R_getContextCall_x 66
-#define R_getContextEnv_x 67
-#define R_getContextFun_x 68
-#define R_getContextSrcRef_x 69
-#define R_getGlobalFunctionContext_x 70
-#define R_getParentFunctionContext_x 71
-#define R_insideBrowser_x 72
-#define R_isEqual_x 73
-#define R_isGlobal_x 74
-#define R_lsInternal3_x 75
-#define R_new_custom_connection_x 76
-#define R_tryEval_x 77
-#define Rf_GetOption1_x 78
-#define Rf_NonNullStringMatch_x 79
-#define Rf_PairToVectorList_x 80
-#define Rf_ScalarDouble_x 81
-#define Rf_ScalarInteger_x 82
-#define Rf_ScalarLogical_x 83
-#define Rf_ScalarString_x 84
-#define Rf_VectorToPairList_x 85
-#define Rf_allocArray_x 86
-#define Rf_allocMatrix_x 87
-#define Rf_allocVector_x 88
-#define Rf_any_duplicated_x 89
-#define Rf_asChar_x 90
-#define Rf_asCharacterFactor_x 91
-#define Rf_asInteger_x 92
-#define Rf_asLogical_x 93
-#define Rf_asReal_x 94
-#define Rf_classgets_x 95
-#define Rf_coerceVector_x 96
-#define Rf_cons_x 97
-#define Rf_copyListMatrix_x 98
-#define Rf_copyMatrix_x 99
-#define Rf_copyMostAttrib_x 100
-#define Rf_defineVar_x 101
-#define Rf_dunif_x 102
-#define Rf_duplicate_x 103
-#define Rf_error_x 104
-#define Rf_errorcall_x 105
-#define Rf_eval_x 106
-#define Rf_findFun_x 107
-#define Rf_findVar_x 108
-#define Rf_findVarInFrame_x 109
-#define Rf_findVarInFrame3_x 110
-#define Rf_getAttrib_x 111
-#define Rf_gsetVar_x 112
-#define Rf_inherits_x 113
-#define Rf_install_x 114
-#define Rf_installChar_x 115
-#define Rf_isNull_x 116
-#define Rf_isString_x 117
-#define Rf_lengthgets_x 118
-#define Rf_match_x 119
-#define Rf_mkCharLenCE_x 120
-#define Rf_namesgets_x 121
-#define Rf_ncols_x 122
-#define Rf_nrows_x 123
-#define Rf_protect_x 124
-#define Rf_punif_x 125
-#define Rf_qunif_x 126
-#define Rf_runif_x 127
-#define Rf_setAttrib_x 128
-#define Rf_str2type_x 129
-#define Rf_unprotect_x 130
-#define Rf_unprotect_ptr_x 131
-#define Rf_warning_x 132
-#define Rf_warningcall_x 133
-#define Rprintf_x 134
-#define SETCADR_x 135
-#define SETCAR_x 136
-#define SETCDR_x 137
-#define SET_NAMED_FASTR_x 138
-#define SET_RDEBUG_x 139
-#define SET_RSTEP_x 140
-#define SET_S4_OBJECT_x 141
-#define SET_STRING_ELT_x 142
-#define SET_SYMVALUE_x 143
-#define SET_TAG_x 144
-#define SET_TYPEOF_FASTR_x 145
-#define SET_VECTOR_ELT_x 146
-#define STRING_ELT_x 147
-#define SYMVALUE_x 148
-#define TAG_x 149
-#define TYPEOF_x 150
-#define UNSET_S4_OBJECT_x 151
-#define VECTOR_ELT_x 152
-#define forceSymbols_x 153
-#define getCCallable_x 154
-#define getConnectionClassString_x 155
-#define getOpenModeString_x 156
-#define getSummaryDescription_x 157
-#define isSeekable_x 158
-#define registerCCallable_x 159
-#define registerRoutines_x 160
-#define setDotSymbolValues_x 161
-#define unif_rand_x 162
-#define useDynamicSymbols_x 163
+#define CLOENV_x 6
+#define COMPLEX_x 7
+#define DUPLICATE_ATTRIB_x 8
+#define ENCLOS_x 9
+#define FASTR_getConnectionChar_x 10
+#define GetRNGstate_x 11
+#define INTEGER_x 12
+#define IS_S4_OBJECT_x 13
+#define LENGTH_x 14
+#define LOGICAL_x 15
+#define NAMED_x 16
+#define OBJECT_x 17
+#define PRCODE_x 18
+#define PRENV_x 19
+#define PRINTNAME_x 20
+#define PRSEEN_x 21
+#define PRVALUE_x 22
+#define PutRNGstate_x 23
+#define RAW_x 24
+#define RDEBUG_x 25
+#define REAL_x 26
+#define RSTEP_x 27
+#define R_BaseEnv_x 28
+#define R_BaseNamespace_x 29
+#define R_BindingIsLocked_x 30
+#define R_CHAR_x 31
+#define R_CleanUp_x 32
+#define R_ExternalPtrAddr_x 33
+#define R_ExternalPtrProtected_x 34
+#define R_ExternalPtrTag_x 35
+#define R_FindNamespace_x 36
+#define R_GetConnection_x 37
+#define R_GlobalContext_x 38
+#define R_GlobalEnv_x 39
+#define R_Home_x 40
+#define R_HomeDir_x 41
+#define R_Interactive_x 42
+#define R_MakeExternalPtr_x 43
+#define R_MethodsNamespace_x 44
+#define R_NamespaceRegistry_x 45
+#define R_NewHashedEnv_x 46
+#define R_ParseVector_x 47
+#define R_PreserveObject_x 48
+#define R_PromiseExpr_x 49
+#define R_ProtectWithIndex_x 50
+#define R_ReadConnection_x 51
+#define R_ReleaseObject_x 52
+#define R_Reprotect_x 53
+#define R_SetExternalPtrAddr_x 54
+#define R_SetExternalPtrProtected_x 55
+#define R_SetExternalPtrTag_x 56
+#define R_TempDir_x 57
+#define R_ToplevelExec_x 58
+#define R_WriteConnection_x 59
+#define R_alloc_x 60
+#define R_compute_identical_x 61
+#define R_do_MAKE_CLASS_x 62
+#define R_do_new_object_x 63
+#define R_do_slot_x 64
+#define R_do_slot_assign_x 65
+#define R_getClassDef_x 66
+#define R_getContextCall_x 67
+#define R_getContextEnv_x 68
+#define R_getContextFun_x 69
+#define R_getContextSrcRef_x 70
+#define R_getGlobalFunctionContext_x 71
+#define R_getParentFunctionContext_x 72
+#define R_has_slot_x 73
+#define R_insideBrowser_x 74
+#define R_isEqual_x 75
+#define R_isGlobal_x 76
+#define R_lsInternal3_x 77
+#define R_new_custom_connection_x 78
+#define R_tryEval_x 79
+#define Rf_GetOption1_x 80
+#define Rf_NonNullStringMatch_x 81
+#define Rf_PairToVectorList_x 82
+#define Rf_ScalarDouble_x 83
+#define Rf_ScalarInteger_x 84
+#define Rf_ScalarLogical_x 85
+#define Rf_ScalarString_x 86
+#define Rf_VectorToPairList_x 87
+#define Rf_allocArray_x 88
+#define Rf_allocMatrix_x 89
+#define Rf_allocVector_x 90
+#define Rf_any_duplicated_x 91
+#define Rf_asChar_x 92
+#define Rf_asCharacterFactor_x 93
+#define Rf_asInteger_x 94
+#define Rf_asLogical_x 95
+#define Rf_asReal_x 96
+#define Rf_classgets_x 97
+#define Rf_coerceVector_x 98
+#define Rf_cons_x 99
+#define Rf_copyListMatrix_x 100
+#define Rf_copyMatrix_x 101
+#define Rf_copyMostAttrib_x 102
+#define Rf_defineVar_x 103
+#define Rf_dunif_x 104
+#define Rf_duplicate_x 105
+#define Rf_error_x 106
+#define Rf_errorcall_x 107
+#define Rf_eval_x 108
+#define Rf_findFun_x 109
+#define Rf_findVar_x 110
+#define Rf_findVarInFrame_x 111
+#define Rf_findVarInFrame3_x 112
+#define Rf_getAttrib_x 113
+#define Rf_gsetVar_x 114
+#define Rf_inherits_x 115
+#define Rf_install_x 116
+#define Rf_installChar_x 117
+#define Rf_isNull_x 118
+#define Rf_isString_x 119
+#define Rf_lengthgets_x 120
+#define Rf_match_x 121
+#define Rf_mkCharLenCE_x 122
+#define Rf_namesgets_x 123
+#define Rf_ncols_x 124
+#define Rf_nrows_x 125
+#define Rf_protect_x 126
+#define Rf_punif_x 127
+#define Rf_qunif_x 128
+#define Rf_runif_x 129
+#define Rf_setAttrib_x 130
+#define Rf_str2type_x 131
+#define Rf_unprotect_x 132
+#define Rf_unprotect_ptr_x 133
+#define Rf_warning_x 134
+#define Rf_warningcall_x 135
+#define Rprintf_x 136
+#define SETCADR_x 137
+#define SETCAR_x 138
+#define SETCDR_x 139
+#define SET_NAMED_FASTR_x 140
+#define SET_RDEBUG_x 141
+#define SET_RSTEP_x 142
+#define SET_S4_OBJECT_x 143
+#define SET_STRING_ELT_x 144
+#define SET_SYMVALUE_x 145
+#define SET_TAG_x 146
+#define SET_TYPEOF_FASTR_x 147
+#define SET_VECTOR_ELT_x 148
+#define STRING_ELT_x 149
+#define SYMVALUE_x 150
+#define TAG_x 151
+#define TYPEOF_x 152
+#define UNSET_S4_OBJECT_x 153
+#define VECTOR_ELT_x 154
+#define forceSymbols_x 155
+#define getCCallable_x 156
+#define getConnectionClassString_x 157
+#define getOpenModeString_x 158
+#define getSummaryDescription_x 159
+#define isSeekable_x 160
+#define registerCCallable_x 161
+#define registerRoutines_x 162
+#define setDotSymbolValues_x 163
+#define unif_rand_x 164
+#define useDynamicSymbols_x 165
 
-#define UPCALLS_TABLE_SIZE 164
+#define UPCALLS_TABLE_SIZE 166
 
 #endif // RFFI_UPCALLSINDEX_H
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h
index 809858dbd944a44b59045e550cc123bed517cfd1..cb21b7d4024ae51fc7e398f75664c70bcc9a12b2 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h
@@ -406,14 +406,14 @@ SEXP Rf_setAttrib(SEXP vec, SEXP name, SEXP val) {
 }
 
 SEXP Rf_duplicate(SEXP x) {
-    TRACE0();
+    TRACE(TARGp, x);
     SEXP result = ((call_Rf_duplicate) callbacks[Rf_duplicate_x])(x, 1);
     checkExitCall();
     return result;
 }
 
 SEXP Rf_shallow_duplicate(SEXP x) {
-    TRACE0();
+    TRACE(TARGp, x);
     SEXP result = ((call_Rf_duplicate) callbacks[Rf_duplicate_x])(x, 0);
     checkExitCall();
     return result;
@@ -770,8 +770,8 @@ SEXP BODY(SEXP x) {
 }
 
 SEXP CLOENV(SEXP x) {
-    TRACE0();
-    return unimplemented("CLOENV");
+    TRACE(TARGp, x);
+    return ((call_CLOENV) callbacks[CLOENV_x])(x);
 }
 
 int RDEBUG(SEXP x) {
@@ -1496,9 +1496,10 @@ SEXP R_do_slot_assign(SEXP obj, SEXP name, SEXP value) {
 }
 
 int R_has_slot(SEXP obj, SEXP name) {
-    TRACE0();
-    unimplemented("R_has_slot");
-    return 0;
+    TRACE(TARGpp, obj, name);
+    SEXP result = ((call_R_has_slot) callbacks[R_has_slot_x])(obj, name);
+    checkExitCall();
+    return result;
 }
 
 SEXP R_do_MAKE_CLASS(const char *what) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
index 9c105a0af64c45af2c5b086f125805916f891f3a..f79c4b8ee45edc89c3bca01d9d63f4063ad15503 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
@@ -53,8 +53,9 @@ import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.GetFunctionEnvironmentNode;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.base.EnvFunctionsFactory.CopyNodeGen;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
@@ -361,17 +362,8 @@ public class EnvFunctions {
          */
         @Specialization
         protected Object environment(RFunction fun,
-                        @Cached("createBinaryProfile()") ConditionProfile noEnvProfile,
-                        @Cached("createBinaryProfile()") ConditionProfile createProfile) {
-            Frame enclosing = fun.getEnclosingFrame();
-            if (noEnvProfile.profile(enclosing == null)) {
-                return RNull.instance;
-            }
-            REnvironment env = RArguments.getEnvironment(enclosing);
-            if (createProfile.profile(env == null)) {
-                return REnvironment.createEnclosingEnvironments(enclosing.materialize());
-            }
-            return env;
+                        @Cached("create()") GetFunctionEnvironmentNode getEnv) {
+            return getEnv.getEnvironment(fun);
         }
 
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java
index 60a62338d6d531bb9243592afa0d452fc68309f1..0217616485e7b4cf7bf947ac312ab3cd7e495d20 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java
@@ -27,8 +27,8 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RecordGraphics.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RecordGraphics.java
index 90b72ab45c609ecbe571b1c436e4762bc55f5451..609b4ef0f0daf3355698e3b331c98fe5a87a1cca 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RecordGraphics.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RecordGraphics.java
@@ -28,8 +28,8 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
index a7744efb76a1dd7fcab5b153ab7c4510f3141605..3c8732d922e0d5f4c7c7e3f0e3dfab119c77c359 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.runtime.RError.Message.INVALID_ENVIRONMENT_SPECIFIED;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
@@ -31,11 +33,9 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.library.methods.SubstituteDirect;
 import com.oracle.truffle.r.nodes.RASTUtils;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import com.oracle.truffle.r.nodes.builtin.EnvironmentNodes.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
-import static com.oracle.truffle.r.runtime.RError.Message.INVALID_ENVIRONMENT_SPECIFIED;
 import com.oracle.truffle.r.runtime.RSubstitute;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RLanguage;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
index c0f82b72babaf1c8b67175a6028a7f3ac5a56210..182e05aa0f9cb3c097906931ead5ea775bb93bfc 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
@@ -12,92 +12,35 @@
  */
 package com.oracle.truffle.r.nodes.access;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.attributes.GetAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetClassAttributeNode;
-import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
-import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
 import com.oracle.truffle.r.nodes.function.ImplicitClassHierarchyNode;
-import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 // Transcribed from src/main/attrib.c file (R_do_slot function)
 
 /**
  * Perform a slot access. This node represents the {@code @} operator in R.
  */
-public abstract class AccessSlotNode extends RBaseNode {
-
-    public abstract Object executeAccess(Object o, String name);
-
-    @Child private ClassHierarchyNode classHierarchy;
-
-    private final BranchProfile noSlot = BranchProfile.create();
-    private final BranchProfile symbolValue = BranchProfile.create();
-    private final boolean asOperator;
+public abstract class AccessSlotNode extends BaseAccessSlotNode {
 
     public AccessSlotNode(boolean asOperator) {
-        this.asOperator = asOperator;
+        super(asOperator);
     }
 
-    protected GetAttributeNode createAttrAccess() {
-        return GetAttributeNode.create();
-    }
-
-    private Object getSlotS4Internal(RAttributable object, String name, Object value, GetClassAttributeNode getClassNode) {
-        if (value == null) {
-            noSlot.enter();
-            assert Utils.isInterned(name);
-            if (name == RRuntime.DOT_S3_CLASS) {
-                if (classHierarchy == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    classHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
-                }
-                return classHierarchy.execute(object);
-            } else if (name == RRuntime.DOT_DATA) {
-                // TODO: any way to cache it or use a mechanism similar to overrides?
-                REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
-                RFunction dataPart = getDataPartFunction(methodsNamespace);
-                return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), true, null, object);
-            } else if (name == RRuntime.NAMES_ATTR_KEY && object instanceof RAbstractVector) {
-                assert false; // RS4Object can never be a vector?
-                return RNull.instance;
-            }
-
-            CompilerDirectives.transferToInterpreter();
-            RStringVector classAttr = getClassNode.getClassAttr(object);
-            if (classAttr == null) {
-                throw error(RError.Message.SLOT_CANNOT_GET, name, TypeofNode.getTypeof(object).getName());
-            } else {
-                throw error(RError.Message.SLOT_NONE, name, classAttr.getLength() == 0 ? RRuntime.STRING_NA : classAttr.getDataAt(0));
-            }
-        }
-        if (value instanceof RSymbol) {
-            symbolValue.enter();
-            if (((RSymbol) value).getName() == RRuntime.PSEUDO_NULL.getName()) {
-                return RNull.instance;
-            }
-        }
-        return value;
-    }
+    public abstract Object executeAccess(Object o, String name);
 
     @Specialization
     protected Object getSlotS4(@SuppressWarnings("unused") RNull object, String name) {
@@ -114,13 +57,6 @@ public abstract class AccessSlotNode extends RBaseNode {
         return getSlotS4Internal(object, internedName, value, getClassNode);
     }
 
-    @TruffleBoundary
-    private static RFunction getDataPartFunction(REnvironment methodsNamespace) {
-        String name = "getDataPart";
-        Object f = methodsNamespace.findFunction(name);
-        return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, f);
-    }
-
     @Specialization(guards = {"!slotAccessAllowed(object)", "isDotData(name)"})
     protected Object getSlotNonS4(RAttributable object, @SuppressWarnings("unused") String name) {
         // TODO: any way to cache it or use a mechanism similar to overrides?
@@ -144,13 +80,4 @@ public abstract class AccessSlotNode extends RBaseNode {
             throw error(RError.Message.SLOT_NON_S4, name, classAttr.getDataAt(0));
         }
     }
-
-    protected boolean isDotData(String name) {
-        assert Utils.isInterned(name);
-        return name == RRuntime.DOT_DATA;
-    }
-
-    protected boolean slotAccessAllowed(RAttributable object) {
-        return object.isS4() || !asOperator;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseAccessSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseAccessSlotNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..6cabf34005e0b3053f3839dbc28194a351121a99
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseAccessSlotNode.java
@@ -0,0 +1,95 @@
+package com.oracle.truffle.r.nodes.access;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.RASTUtils;
+import com.oracle.truffle.r.nodes.attributes.GetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetClassAttributeNode;
+import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
+import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
+import com.oracle.truffle.r.nodes.unary.TypeofNode;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+public abstract class BaseAccessSlotNode extends RBaseNode {
+
+    @Child private ClassHierarchyNode classHierarchy;
+
+    private final BranchProfile noSlot = BranchProfile.create();
+    private final BranchProfile symbolValue = BranchProfile.create();
+    protected final boolean asOperator;
+
+    protected BaseAccessSlotNode(boolean asOperator) {
+        this.asOperator = asOperator;
+    }
+
+    @TruffleBoundary
+    protected static RFunction getDataPartFunction(REnvironment methodsNamespace) {
+        String name = "getDataPart";
+        Object f = methodsNamespace.findFunction(name);
+        return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, f);
+    }
+
+    protected GetAttributeNode createAttrAccess() {
+        return GetAttributeNode.create();
+    }
+
+    protected Object getSlotS4Internal(RAttributable object, String name, Object value, GetClassAttributeNode getClassNode) {
+        if (value == null) {
+            noSlot.enter();
+            assert Utils.isInterned(name);
+            if (name == RRuntime.DOT_S3_CLASS) {
+                if (classHierarchy == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    classHierarchy = insert(ClassHierarchyNodeGen.create(true, false));
+                }
+                return classHierarchy.execute(object);
+            } else if (name == RRuntime.DOT_DATA) {
+                // TODO: any way to cache it or use a mechanism similar to overrides?
+                REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
+                RFunction dataPart = getDataPartFunction(methodsNamespace);
+                return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), true, null, object);
+            } else if (name == RRuntime.NAMES_ATTR_KEY && object instanceof RAbstractVector) {
+                assert false; // RS4Object can never be a vector?
+                return RNull.instance;
+            }
+
+            CompilerDirectives.transferToInterpreter();
+            RStringVector classAttr = getClassNode.getClassAttr(object);
+            if (classAttr == null) {
+                throw error(RError.Message.SLOT_CANNOT_GET, name, TypeofNode.getTypeof(object).getName());
+            } else {
+                throw error(RError.Message.SLOT_NONE, name, classAttr.getLength() == 0 ? RRuntime.STRING_NA : classAttr.getDataAt(0));
+            }
+        }
+        if (value instanceof RSymbol) {
+            symbolValue.enter();
+            if (((RSymbol) value).getName() == RRuntime.PSEUDO_NULL.getName()) {
+                return RNull.instance;
+            }
+        }
+        return value;
+    }
+
+    protected boolean isDotData(String name) {
+        assert Utils.isInterned(name);
+        return name == RRuntime.DOT_DATA;
+    }
+
+    protected boolean slotAccessAllowed(RAttributable object) {
+        return object.isS4() || !asOperator;
+    }
+
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/HasSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/HasSlotNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..60ac13f14f142a5f7879931eb8f51c142e8fba20
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/HasSlotNode.java
@@ -0,0 +1,76 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995, 1996, 1997  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1995-2014, The R Core Team
+ * Copyright (c) 2002-2008, The R Foundation
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.access;
+
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.RASTUtils;
+import com.oracle.truffle.r.nodes.attributes.GetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+// Transcribed from src/main/attrib.c file (R_has_slot function)
+
+/**
+ * Perform a check if the specified slot exists. This is almost like the {@code @} operator in R but
+ * does not raise an error if the slot does not exist.
+ */
+public abstract class HasSlotNode extends BaseAccessSlotNode {
+
+    public HasSlotNode(boolean asOperator) {
+        super(asOperator);
+    }
+
+    public abstract boolean executeAccess(Object o, String name);
+
+    @Specialization
+    @SuppressWarnings("unused")
+    protected boolean getSlotS4(RNull object, String name) {
+        return false;
+    }
+
+    @Specialization(guards = {"slotAccessAllowed(object)"})
+    protected boolean getSlotS4Cached(RAttributable object, String name,
+                    @Cached("createAttrAccess()") GetAttributeNode attrAccess,
+                    @Cached("create()") InitAttributesNode initAttrNode) {
+        return attrAccess.execute(initAttrNode.execute(object), name) != null;
+    }
+
+    @Specialization(guards = {"!slotAccessAllowed(object)", "isDotData(name)"})
+    protected boolean getSlotNonS4(RAttributable object, @SuppressWarnings("unused") String name) {
+        // TODO: any way to cache it or use a mechanism similar to overrides?
+        REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
+        RFunction dataPart = getDataPartFunction(methodsNamespace);
+
+        // TODO will throw an error if ".data" does not exists but should not
+        Object result = RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), true, null, object);
+        return result != null;
+    }
+
+    // this is really a fallback specialization but @Fallback does not work here (because of the
+    // type of "object"?)
+    @Specialization(guards = {"!slotAccessAllowed(object)", "!isDotData(name)"})
+    @SuppressWarnings("unused")
+    protected boolean getSlot(RAttributable object, String name) {
+        return false;
+    }
+
+    public static HasSlotNode create(boolean asOperator) {
+        return HasSlotNodeGen.create(asOperator);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/EnvironmentNodes.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/EnvironmentNodes.java
new file mode 100644
index 0000000000000000000000000000000000000000..8640243b9310348256968a7912d253714e202815
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/EnvironmentNodes.java
@@ -0,0 +1,77 @@
+package com.oracle.truffle.r.nodes.builtin;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+public final class EnvironmentNodes {
+
+    /**
+     * Abstracted for use by other nodes that need to convert a list into an environment.
+     */
+    public static final class RList2EnvNode extends RBaseNode {
+        private final boolean ignoreMissingNames;
+
+        public RList2EnvNode() {
+            this(false);
+        }
+
+        public RList2EnvNode(boolean ignoreMissingNames) {
+            this.ignoreMissingNames = ignoreMissingNames;
+        }
+
+        @TruffleBoundary
+        public REnvironment execute(RAbstractListVector list, REnvironment env) {
+            if (list.getLength() == 0) {
+                return env;
+            }
+            RStringVector names = list.getNames();
+            if (names == null) {
+                throw error(RError.Message.LIST_NAMES_SAME_LENGTH);
+            }
+            for (int i = list.getLength() - 1; i >= 0; i--) {
+                String name = names.getDataAt(i);
+                if (!ignoreMissingNames && name.length() == 0) {
+                    throw error(RError.Message.ZERO_LENGTH_VARIABLE);
+                }
+                // in case of duplicates, last element in list wins
+                if (env.get(name) == null) {
+                    env.safePut(name, list.getDataAt(i));
+                }
+            }
+            return env;
+        }
+    }
+
+    public static final class GetFunctionEnvironmentNode extends RBaseNode {
+        private final ConditionProfile noEnvProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile createProfile = ConditionProfile.createBinaryProfile();
+
+        /**
+         * Returns the environment that {@code func} was created in.
+         */
+        public Object getEnvironment(RFunction fun) {
+            Frame enclosing = fun.getEnclosingFrame();
+            if (noEnvProfile.profile(enclosing == null)) {
+                return RNull.instance;
+            }
+            REnvironment env = RArguments.getEnvironment(enclosing);
+            if (createProfile.profile(env == null)) {
+                return REnvironment.createEnclosingEnvironments(enclosing.materialize());
+            }
+            return env;
+        }
+
+        public static GetFunctionEnvironmentNode create() {
+            return new GetFunctionEnvironmentNode();
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java
deleted file mode 100644
index c7491ac86ccd76ac703d9ced6cde723c6445fe99..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java
+++ /dev/null
@@ -1,67 +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.nodes.builtin;
-
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-
-/**
- * Abstracted for use by other nodes that need to convert a list into an environment.
- */
-public final class RList2EnvNode extends RBaseNode {
-    private final boolean ignoreMissingNames;
-
-    public RList2EnvNode() {
-        this(false);
-    }
-
-    public RList2EnvNode(boolean ignoreMissingNames) {
-        this.ignoreMissingNames = ignoreMissingNames;
-    }
-
-    @TruffleBoundary
-    public REnvironment execute(RAbstractListVector list, REnvironment env) {
-        if (list.getLength() == 0) {
-            return env;
-        }
-        RStringVector names = list.getNames();
-        if (names == null) {
-            throw error(RError.Message.LIST_NAMES_SAME_LENGTH);
-        }
-        for (int i = list.getLength() - 1; i >= 0; i--) {
-            String name = names.getDataAt(i);
-            if (!ignoreMissingNames && name.length() == 0) {
-                throw error(RError.Message.ZERO_LENGTH_VARIABLE);
-            }
-            // in case of duplicates, last element in list wins
-            if (env.get(name) == null) {
-                env.safePut(name, list.getDataAt(i));
-            }
-        }
-        return env;
-    }
-}