diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
index 65a38e8148ec40896a988c3f9e4267e95c857cb0..d7273682607ed2bd9e1daa9848bd489095bbed36 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
@@ -31,6 +31,7 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.rawValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.typeName;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.RVisibility.ON;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
@@ -50,6 +51,7 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.ArityException;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
@@ -66,6 +68,7 @@ import com.oracle.truffle.api.source.Source.Builder;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
@@ -104,7 +107,7 @@ public class FastRInterop {
         isTesting = true;
     }
 
-    @RBuiltin(name = "eval.external", visibility = OFF, kind = PRIMITIVE, parameterNames = {"mimeType", "source", "path"}, behavior = COMPLEX)
+    @RBuiltin(name = "eval.external", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"mimeType", "source", "path"}, behavior = COMPLEX)
     public abstract static class Eval extends RBuiltinNode.Arg3 {
 
         static {
@@ -114,22 +117,37 @@ public class FastRInterop {
             casts.arg("path").allowMissing().mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
         }
 
+        @Child private SetVisibilityNode setVisibilityNode = SetVisibilityNode.create();
+        @Child private Foreign2R foreign2rNode = Foreign2R.create();
+
         protected DirectCallNode createCall(String mimeType, String source) {
             return Truffle.getRuntime().createDirectCallNode(parse(mimeType, source));
         }
 
         @SuppressWarnings("unused")
         @Specialization(guards = {"cachedMimeType != null", "cachedMimeType.equals(mimeType)", "cachedSource != null", "cachedSource.equals(source)"})
-        protected Object evalCached(String mimeType, String source, RMissing path,
+        protected Object evalCached(VirtualFrame frame, String mimeType, String source, RMissing path,
                         @Cached("mimeType") String cachedMimeType,
                         @Cached("source") String cachedSource,
                         @Cached("createCall(mimeType, source)") DirectCallNode call) {
-            return call.call(EMPTY_OBJECT_ARRAY);
+            try {
+                return foreign2rNode.execute(call.call(EMPTY_OBJECT_ARRAY));
+            } finally {
+                setVisibilityNode.execute(frame, true);
+            }
         }
 
         @Specialization(replaces = "evalCached")
+        protected Object eval(VirtualFrame frame, String mimeType, String source, @SuppressWarnings("unused") RMissing path) {
+            try {
+                return foreign2rNode.execute(parseAndCall(source, mimeType));
+            } finally {
+                setVisibilityNode.execute(frame, true);
+            }
+        }
+
         @TruffleBoundary
-        protected Object eval(String mimeType, String source, @SuppressWarnings("unused") RMissing path) {
+        private Object parseAndCall(String source, String mimeType) {
             return parse(mimeType, source).call();
         }
 
@@ -142,7 +160,7 @@ public class FastRInterop {
         protected CallTarget parse(String mimeType, String source) {
             CompilerAsserts.neverPartOfCompilation();
 
-            Source sourceObject = RSource.fromTextInternal(source, RSource.Internal.EVAL_WRAPPER, mimeType);
+            Source sourceObject = RSource.fromTextInternalInvisible(source, RSource.Internal.EVAL_WRAPPER, mimeType);
             try {
                 return RContext.getInstance().getEnv().parse(sourceObject);
             } catch (Throwable t) {
@@ -151,21 +169,35 @@ public class FastRInterop {
         }
 
         @Specialization
-        @TruffleBoundary
-        protected Object eval(String mimeType, @SuppressWarnings("unused") String source, String path) {
-            return parseFile(path, mimeType).call();
+        protected Object eval(VirtualFrame frame, String mimeType, @SuppressWarnings("unused") String source, String path) {
+            try {
+                return foreign2rNode.execute(parseFileAndCall(path, mimeType));
+            } finally {
+                setVisibilityNode.execute(frame, false);
+            }
         }
 
         @Specialization
-        @TruffleBoundary
-        protected Object eval(String mimeType, @SuppressWarnings("unused") RMissing source, String path) {
-            return parseFile(path, mimeType).call();
+        protected Object eval(VirtualFrame frame, String mimeType, @SuppressWarnings("unused") RMissing source, String path) {
+            try {
+                return foreign2rNode.execute(parseFileAndCall(path, mimeType));
+            } finally {
+                setVisibilityNode.execute(frame, false);
+            }
         }
 
         @Specialization
+        protected Object eval(VirtualFrame frame, @SuppressWarnings("unused") RMissing mimeType, @SuppressWarnings("unused") RMissing source, String path) {
+            try {
+                return foreign2rNode.execute(parseFileAndCall(path, null));
+            } finally {
+                setVisibilityNode.execute(frame, false);
+            }
+        }
+
         @TruffleBoundary
-        protected Object eval(@SuppressWarnings("unused") RMissing mimeType, @SuppressWarnings("unused") RMissing source, String path) {
-            return parseFile(path, null).call();
+        private Object parseFileAndCall(String path, String mimeType) {
+            return parseFile(path, mimeType).call();
         }
 
         protected CallTarget parseFile(String path, String mimeType) {