From 223fb6f4323dfa8817fd03591dd72297bf022ec5 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Tue, 16 Jan 2018 20:17:26 +0100
Subject: [PATCH] Revive the GNU R compatible embedding API with NFI backend

---
 .../r/engine/TruffleRLanguageImpl.java        |   6 +-
 .../engine/shell/EmbeddedConsoleHandler.java  | 275 +++++---
 .../truffle/r/engine/shell/REmbedded.java     |  61 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Context.java  |   2 +-
 .../r/ffi/impl/llvm/TruffleLLVM_REmbed.java   |  25 +-
 .../ffi/impl/managed/Managed_REmbedRFFI.java  |  19 +-
 .../r/ffi/impl/nfi/TruffleNFI_Context.java    |   2 +-
 .../r/ffi/impl/nfi/TruffleNFI_REmbed.java     |  75 +-
 .../truffle/r/ffi/processor/FFIProcessor.java |   4 +-
 .../r/launcher/DelegatingConsoleHandler.java  |  34 +
 .../oracle/truffle/r/launcher/RCommand.java   |  21 +-
 .../truffle/r/launcher/RStartParams.java      |  44 +-
 .../truffle/r/launcher/RscriptCommand.java    |   4 +-
 com.oracle.truffle.r.native/fficall/Makefile  |   6 +-
 .../fficall/src/common/rffi_upcalls.h         |  13 +-
 .../fficall/src/truffle_common/Rembedded.c    | 660 -----------------
 .../fficall/src/truffle_nfi/Makefile          |   5 +-
 .../fficall/src/truffle_nfi/Rembedded.c       | 662 +++++++++++++++++-
 .../r/nodes/builtin/RBuiltinPackages.java     |   8 +-
 .../truffle/r/nodes/builtin/base/Quit.java    |   4 +-
 .../base/system/SystemFunctionFactory.java    |   5 +-
 .../nodes/builtin/helpers/TraceHandling.java  |   4 +-
 .../r/runtime/LazyResourceHandlerFactory.java |   3 +-
 .../oracle/truffle/r/runtime/RCleanUp.java    |  52 +-
 .../oracle/truffle/r/runtime/REnvVars.java    |   2 +-
 .../com/oracle/truffle/r/runtime/RError.java  |   2 +-
 .../r/runtime/RInterfaceCallbacks.java        |   7 +-
 .../truffle/r/runtime/RInternalError.java     |   4 +-
 .../oracle/truffle/r/runtime/RPlatform.java   |   4 +-
 .../oracle/truffle/r/runtime/RProfile.java    |   4 +-
 .../oracle/truffle/r/runtime/RSuicide.java    | 109 +++
 .../truffle/r/runtime/TempPathName.java       |   4 +-
 .../com/oracle/truffle/r/runtime/Utils.java   |  34 +-
 .../r/runtime/conn/StdConnections.java        |   4 +-
 .../truffle/r/runtime/env/REnvironment.java   |   4 +-
 .../com/oracle/truffle/r/runtime/ffi/DLL.java |  26 +-
 .../truffle/r/runtime/ffi/NativeFunction.java |   6 +
 .../truffle/r/runtime/ffi/REmbedRFFI.java     |  36 +-
 .../truffle/r/runtime/ffi/RFFIFactory.java    |   8 +-
 .../embedded/src/main.c                       |  11 +-
 .../com/oracle/truffle/r/test/TestBase.java   |   4 +-
 mx.fastr/copyrights/overrides                 |   2 +-
 42 files changed, 1297 insertions(+), 968 deletions(-)
 create mode 100644 com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java
 delete mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
index 57e5be131a..e47345b704 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -44,7 +44,7 @@ import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RAccuracyInfo;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -83,7 +83,7 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage {
              * Utils.rSuicide does. For now we catch it and exit the process.
              */
             try {
-                Utils.rSuicide("error during R language initialization");
+                RSuicide.rSuicide("error during R language initialization");
             } catch (ExitException ex) {
                 System.exit(ex.getStatus());
             }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
index 5b9c747e20..d0249e7a18 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -22,13 +22,25 @@
  */
 package com.oracle.truffle.r.engine.shell;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.function.Supplier;
+
+import org.graalvm.polyglot.Context;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.r.launcher.ConsoleHandler;
-import com.oracle.truffle.r.launcher.DefaultConsoleHandler;
-import com.oracle.truffle.r.launcher.JLineConsoleHandler;
-import com.oracle.truffle.r.launcher.RStartParams;
+import com.oracle.truffle.r.launcher.DelegatingConsoleHandler;
 import com.oracle.truffle.r.runtime.RInterfaceCallbacks;
-import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
+import com.oracle.truffle.r.runtime.ffi.REmbedRFFI.ReadConsoleNode;
+import com.oracle.truffle.r.runtime.ffi.REmbedRFFI.WriteConsoleBaseNode;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 /**
@@ -38,99 +50,192 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
  * N.B. At the time the constructor is created, we do not know if the console is overridden so we
  * have be lazy about that.
  *
+ * Since we do not have access to FastR internals in the launcher project. We're using internal
+ * FastR builtins to implement the functionality.
+ *
  */
-public class EmbeddedConsoleHandler extends ConsoleHandler {
-
-    private final RStartParams startParams;
-    /**
-     * Only not {@code null} when console is not overridden.
-     */
+public final class EmbeddedConsoleHandler extends DelegatingConsoleHandler {
+    private Context context;
+    private Supplier<ConsoleHandler> delegateFactory;
     private ConsoleHandler delegate;
-    private REmbedRFFI rEmbedRFFI;
-    private String prompt;
 
-    EmbeddedConsoleHandler(RStartParams startParams) {
-        this.startParams = startParams;
+    private CallTarget readLineCallTarget;
+    private CallTarget writeCallTarget;
+    private CallTarget writeErrCallTarget;
+
+    @Override
+    public void setContext(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public void setDelegate(Supplier<ConsoleHandler> delegateFactory) {
+        this.delegateFactory = delegateFactory;
+    }
+
+    @SuppressWarnings("try")
+    @Override
+    public String readLine() {
+        try (ContextClose ignored = inContext()) {
+            return isOverridden("R_ReadConsole") ? (String) getReadLineCallTarget().call("TODO prompt>") : getDelegate().readLine();
+        }
     }
 
-    @TruffleBoundary
-    private REmbedRFFI getREmbedRFFI() {
-        if (rEmbedRFFI == null) {
-            rEmbedRFFI = RFFIFactory.getREmbedRFFI();
-            if (!(RInterfaceCallbacks.R_WriteConsole.isOverridden() || RInterfaceCallbacks.R_ReadConsole.isOverridden())) {
-                if (startParams.noReadline()) {
-                    delegate = new DefaultConsoleHandler(System.in, System.out, false);
+    @SuppressWarnings("try")
+    @Override
+    public void setPrompt(String prompt) {
+        try (ContextClose ignored = inContext()) {
+            if (!isOverridden("R_ReadConsole")) {
+                getDelegate().setPrompt(prompt);
+            } else {
+                // TODO: set prompt
+            }
+        }
+    }
+
+    @Override
+    public InputStream createInputStream() {
+        return new InputStream() {
+            private String currentLine;
+            private int currentLineIdx;
+            private InputStream delegateInputStream;
+
+            @SuppressWarnings("try")
+            @Override
+            public int read() throws IOException {
+                try (ContextClose ignored = inContext()) {
+                    return readImpl();
+                }
+            }
+
+            private int readImpl() throws IOException {
+                if (!isOverridden("R_ReadConsole")) {
+                    if (delegateInputStream == null) {
+                        delegateInputStream = getDelegate().createInputStream();
+                    }
+                    return delegateInputStream.read();
+                }
+                if (currentLine == null || currentLineIdx >= currentLine.length()) {
+                    currentLine = readLine();
+                    currentLineIdx = 0;
+                    if (currentLine == null) {
+                        return 0;
+                    }
+                }
+                return currentLine.charAt(currentLineIdx++);
+            }
+        };
+    }
+
+    public OutputStream createStdOutputStream(OutputStream defaultValue) {
+        return createOutputSteam(this::getWriteCallTarget, defaultValue);
+    }
+
+    public OutputStream createErrOutputStream(OutputStream defaultValue) {
+        return createOutputSteam(this::getWriteErrCallTarget, defaultValue);
+    }
+
+    private OutputStream createOutputSteam(Supplier<CallTarget> writeCallTarget, OutputStream defaultStream) {
+        return new BufferedOutputStream(new EmbeddedConsoleOutputSteam(writeCallTarget, defaultStream), 128);
+    }
+
+    private boolean isOverridden(String name) {
+        RInterfaceCallbacks clbk = RInterfaceCallbacks.valueOf(name);
+        return clbk.isOverridden();
+    }
+
+    private final class EmbeddedConsoleOutputSteam extends OutputStream {
+        private final OutputStream delegate;
+        private Supplier<CallTarget> writeCallTarget;
+
+        EmbeddedConsoleOutputSteam(Supplier<CallTarget> writeCallTarget, OutputStream delegate) {
+            this.writeCallTarget = writeCallTarget;
+            this.delegate = delegate;
+        }
+
+        @SuppressWarnings("try")
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException {
+            // Note: R_WriteConsole callback is (seemingly...) used for both stdout and stderr
+            try (ContextClose ignored = inContext()) {
+                if (!isOverridden("R_WriteConsole")) {
+                    delegate.write(b, off, len);
                 } else {
-                    delegate = new JLineConsoleHandler(System.in, System.out, startParams.isSlave());
+                    String str = StandardCharsets.US_ASCII.decode(ByteBuffer.wrap(b, off, len)).toString();
+                    writeCallTarget.get().call(str);
                 }
             }
         }
-        return rEmbedRFFI;
-    }
-
-    // @Override
-    // @TruffleBoundary
-    // public void println(String s) {
-    // getREmbedRFFI();
-    // if (delegate == null) {
-    // getREmbedRFFI().writeConsole(s);
-    // getREmbedRFFI().writeConsole("\n");
-    // } else {
-    // delegate.println(s);
-    // }
-    // }
-    //
-    // @Override
-    // @TruffleBoundary
-    // public void print(String s) {
-    // getREmbedRFFI();
-    // if (delegate == null) {
-    // rEmbedRFFI.writeConsole(s);
-    // } else {
-    // delegate.print(s);
-    // }
-    // }
-    //
-    // @Override
-    // @TruffleBoundary
-    // public void printErrorln(String s) {
-    // getREmbedRFFI();
-    // if (delegate == null) {
-    // rEmbedRFFI.writeErrConsole(s);
-    // rEmbedRFFI.writeErrConsole("\n");
-    // } else {
-    // delegate.printErrorln(s);
-    // }
-    // }
-    //
-    // @Override
-    // @TruffleBoundary
-    // public void printError(String s) {
-    // getREmbedRFFI();
-    // if (delegate == null) {
-    // rEmbedRFFI.writeErrConsole(s);
-    // } else {
-    // delegate.printError(s);
-    // }
-    // }
 
-    @Override
-    @TruffleBoundary
-    public String readLine() {
-        getREmbedRFFI();
+        @Override
+        public void write(int b) throws IOException {
+            write(new byte[]{(byte) b}, 0, 1);
+        }
+    }
+
+    private ConsoleHandler getDelegate() {
         if (delegate == null) {
-            return rEmbedRFFI.readConsole(prompt);
-        } else {
-            return delegate.readLine();
+            delegate = delegateFactory.get();
         }
+        return delegate;
     }
 
-    @Override
-    @TruffleBoundary
-    public void setPrompt(String prompt) {
-        this.prompt = prompt;
-        if (delegate != null) {
-            delegate.setPrompt(prompt);
+    private ContextClose inContext() {
+        context.enter();
+        return new ContextClose();
+    }
+
+    private final class ContextClose implements AutoCloseable {
+        @Override
+        public void close() {
+            context.leave();
+        }
+    }
+
+    private CallTarget getReadLineCallTarget() {
+        if (readLineCallTarget == null) {
+            readLineCallTarget = Truffle.getRuntime().createCallTarget(new RootNode(null) {
+                @Child private ReadConsoleNode readConsoleNode = RFFIFactory.getREmbedRFFI().createReadConsoleNode();
+
+                @Override
+                public Object execute(VirtualFrame frame) {
+                    return readConsoleNode.execute((String) frame.getArguments()[0]);
+                }
+            });
+        }
+        return readLineCallTarget;
+    }
+
+    private CallTarget getWriteCallTarget() {
+        if (writeCallTarget == null) {
+            writeCallTarget = createWriteCallTarget(RFFIFactory.getREmbedRFFI().createWriteConsoleNode());
+        }
+        return writeCallTarget;
+    }
+
+    private CallTarget getWriteErrCallTarget() {
+        if (writeErrCallTarget == null) {
+            writeErrCallTarget = createWriteCallTarget(RFFIFactory.getREmbedRFFI().createWriteErrConsoleNode());
+        }
+        return writeErrCallTarget;
+    }
+
+    private CallTarget createWriteCallTarget(WriteConsoleBaseNode writeConsoleNode) {
+        return Truffle.getRuntime().createCallTarget(new WriteOutBaseRootNode(writeConsoleNode));
+    }
+
+    private static final class WriteOutBaseRootNode extends RootNode {
+        @Child private WriteConsoleBaseNode writeNode;
+
+        WriteOutBaseRootNode(WriteConsoleBaseNode writeNode) {
+            super(null);
+            this.writeNode = writeNode;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            writeNode.execute((String) frame.getArguments()[0]);
+            return null;
         }
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
index 7dee1b6ac5..afb26eabdb 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.engine.shell;
 
+import java.io.InputStream;
+import java.io.OutputStream;
+
 import org.graalvm.polyglot.Context;
 import org.graalvm.polyglot.Engine;
 import org.graalvm.polyglot.Source;
 
-import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.r.launcher.ConsoleHandler;
 import com.oracle.truffle.r.launcher.RCmdOptions;
 import com.oracle.truffle.r.launcher.RCommand;
 import com.oracle.truffle.r.launcher.RStartParams;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource.Internal;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.context.ChildContextInfo;
 import com.oracle.truffle.r.runtime.context.RContext;
 
 /**
@@ -53,13 +54,10 @@ import com.oracle.truffle.r.runtime.context.RContext;
  * Rf_mainloop();
  * </pre>
  *
- * {@code Rf_initialize_R} invokes {@link #initializeR(String[])}. This creates an
- * {@link RStartParams} object in {@code embedded} mode that is recorded in the
- * {@link ChildContextInfo} object which is itself stored as a global symbol in the associated
- * {@link PolyglotEngine} instance. The FastR {@link PolyglotEngine} is then partially initialized.
- * The call to {@code R_SetParams} will adjust the values stored in the {@link RStartParams} object
- * and then {@code Rf_mainloop}, which calls {@link #setupRmainloop()} and then
- * {@link #runRmainloop()}, which will complete the FastR initialization and enter the
+ * {@code Rf_initialize_R} invokes {@link #initializeR(String[])}, which creates new polyglot
+ * {@link Context}. The call to {@code R_SetParams} adjusts the values stored in the
+ * {@link RStartParams} object and then {@code Rf_mainloop}, which calls {@link #setupRmainloop()}
+ * and then {@link #runRmainloop()}, which will complete the FastR initialization and enter the
  * read-eval-print loop.
  */
 public class REmbedded {
@@ -78,12 +76,31 @@ public class REmbedded {
         RContext.setEmbedded();
         RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false);
 
-        consoleHandler = RCommand.createConsoleHandler(options, true, System.in, System.out);
-        try (Context cntx = Context.newBuilder().allowHostAccess(true).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(System.out).err(System.err).build()) {
-            context = cntx;
-            consoleHandler.setContext(context);
-            context.eval(INIT);
-        }
+        EmbeddedConsoleHandler embeddedConsoleHandler = new EmbeddedConsoleHandler();
+
+        consoleHandler = RCommand.createConsoleHandler(options, embeddedConsoleHandler, System.in, System.out);
+        InputStream input = consoleHandler.createInputStream();
+        boolean useEmbedded = consoleHandler == embeddedConsoleHandler;
+        OutputStream stdOut = useEmbedded ? embeddedConsoleHandler.createStdOutputStream(System.out) : System.out;
+        OutputStream stdErr = useEmbedded ? embeddedConsoleHandler.createErrOutputStream(System.err) : System.err;
+        context = Context.newBuilder().allowHostAccess(true).arguments("R", options.getArguments()).in(input).out(stdOut).err(stdErr).build();
+        consoleHandler.setContext(context);
+        context.eval(INIT);
+    }
+
+    /**
+     * Adjusts the values stored in {@link RStartParams}. Invoked from the native embedding code,
+     * i.e. not from a down-call, so the callbacks native array is not set-up properly. Moreover,
+     * this call is made during R initialization, so it not entirely clear if the FFI implementation
+     * has been fully initialized yet.
+     */
+    @SuppressWarnings("unused")
+    private static void setParams(boolean quietA, boolean slaveA, boolean interactiveA, boolean verboseA, boolean loadSiteFileA,
+                    boolean loadInitFileA, boolean debugInitFileA, int restoreActionA, int saveActionA, boolean noRenvironA) {
+        context.enter();
+        RStartParams params = RContext.getInstance().getStartParams();
+        params.setParams(quietA, slaveA, interactiveA, verboseA, loadSiteFileA, loadInitFileA, debugInitFileA, restoreActionA, saveActionA, noRenvironA);
+        context.leave();
     }
 
     /**
@@ -106,11 +123,10 @@ public class REmbedded {
      * native code after {@link #initializeR} returned.
      */
     private static void runRmainloop() {
+        context.enter();
         RContext.getInstance().completeEmbeddedInitialization();
-        if (!RContext.getInstance().getStartParams().isQuiet()) {
-            System.out.println(RRuntime.WELCOME_MESSAGE);
-        }
         int status = RCommand.readEvalPrint(context, consoleHandler);
+        context.leave();
         context.close();
         Utils.systemExit(status);
     }
@@ -127,13 +143,14 @@ public class REmbedded {
     // Checkstyle: stop method name check
 
     /**
-     * Upcalled from embedded mode to (really) commit suicide. This provides the default
+     * Upcalled from embedded mode via JNI to (really) commit suicide. This provides the default
      * implementation of the {@code R_Suicide} function in the {@code Rinterface} API. If an
      * embeddee overrides it, it typically will save this value and invoke it after its own
      * customization.
      */
     @SuppressWarnings("unused")
     private static void R_Suicide(String msg) {
-        Utils.rSuicideDefault(msg);
+        RSuicide.rSuicideDefault(msg);
     }
+
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
index c7ddced84a..c9365ee397 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
@@ -57,7 +57,7 @@ final class TruffleLLVM_Context extends RFFIContext {
     public ContextState initialize(RContext context) {
         if (context.isInitial()) {
             String librffiPath = LibPaths.getBuiltinLibPath("R");
-            DLL.loadLibR(librffiPath);
+            DLL.loadLibR(context, librffiPath);
         }
         dllState.initialize(context);
         callState.initialize(context);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java
index d61d63cea4..7fa4235085 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -26,29 +26,18 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
 
 public class TruffleLLVM_REmbed implements REmbedRFFI {
-
-    @Override
-    public void suicide(String x) {
-        throw RInternalError.unimplemented();
-    }
-
-    @Override
-    public void cleanUp(int type, int x, int y) {
-        throw RInternalError.unimplemented();
-    }
-
     @Override
-    public String readConsole(String prompt) {
-        throw RInternalError.unimplemented();
+    public ReadConsoleNode createReadConsoleNode() {
+        throw RInternalError.unimplemented("TODO");
     }
 
     @Override
-    public void writeConsole(String x) {
-        throw RInternalError.unimplemented();
+    public WriteConsoleNode createWriteConsoleNode() {
+        throw RInternalError.unimplemented("TODO");
     }
 
     @Override
-    public void writeErrConsole(String x) {
-        throw RInternalError.unimplemented();
+    public WriteErrConsoleNode createWriteErrConsoleNode() {
+        throw RInternalError.unimplemented("TODO");
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java
index 17295f062a..7a99eaba80 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -24,31 +24,22 @@ package com.oracle.truffle.r.ffi.impl.managed;
 
 import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
 
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
 
 public class Managed_REmbedRFFI implements REmbedRFFI {
     @Override
-    public void suicide(String x) {
+    public ReadConsoleNode createReadConsoleNode() {
         throw unsupported("REmbed");
     }
 
     @Override
-    public void cleanUp(int type, int x, int y) {
+    public WriteConsoleNode createWriteConsoleNode() {
         throw unsupported("REmbed");
     }
 
     @Override
-    public String readConsole(String prompt) {
-        throw unsupported("REmbed");
-    }
-
-    @Override
-    public void writeConsole(String x) {
-        throw unsupported("REmbed");
-    }
-
-    @Override
-    public void writeErrConsole(String x) {
+    public WriteErrConsoleNode createWriteErrConsoleNode() {
         throw unsupported("REmbed");
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
index bf3dd59779..affec6643a 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
@@ -282,7 +282,7 @@ final class TruffleNFI_Context extends RFFIContext {
         try {
             String librffiPath = LibPaths.getBuiltinLibPath("R");
             if (context.isInitial()) {
-                DLL.loadLibR(librffiPath);
+                DLL.loadLibR(context, librffiPath);
             } else {
                 // force initialization of NFI
                 DLLRFFI.DLOpenRootNode.create(context).call(librffiPath, false, false);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
index fec06c845d..64c70971c2 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -22,33 +22,82 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.NativeFunction;
 import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
 
 public class TruffleNFI_REmbed implements REmbedRFFI {
 
-    @Override
-    public void suicide(String x) {
-        throw RInternalError.unimplemented();
+    private static class TruffleNFI_ReadConsoleNode extends TruffleNFI_DownCallNode implements ReadConsoleNode {
+        @Child private Node unboxNode;
+
+        @Override
+        protected NativeFunction getFunction() {
+            return NativeFunction.rembedded_read_console;
+        }
+
+        @Override
+        public String execute(String prompt) {
+            Object result = call(prompt);
+            if (result instanceof String) {
+                return (String) result;
+            }
+            assert result instanceof TruffleObject : "NFI is expected to send us TruffleObject or String";
+            if (unboxNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                unboxNode = insert(Message.UNBOX.createNode());
+            }
+            try {
+                return (String) ForeignAccess.sendUnbox(unboxNode, (TruffleObject) result);
+            } catch (ClassCastException | UnsupportedMessageException e) {
+                CompilerDirectives.transferToInterpreter();
+                throw RInternalError.shouldNotReachHere("Unboxing TruffleObject from NFI, which should be String wrapper, failed. " + e.getMessage());
+            }
+        }
     }
 
-    @Override
-    public void cleanUp(int type, int x, int y) {
-        throw RInternalError.unimplemented();
+    private static class TruffleNFI_WriteConsoleNode extends TruffleNFI_DownCallNode implements WriteConsoleNode {
+        @Override
+        protected NativeFunction getFunction() {
+            return NativeFunction.rembedded_write_console;
+        }
+
+        @Override
+        public void execute(String x) {
+            call(x, x.length());
+        }
+    }
+
+    private static class TruffleNFI_WriteErrConsoleNode extends TruffleNFI_DownCallNode implements WriteErrConsoleNode {
+        @Override
+        protected NativeFunction getFunction() {
+            return NativeFunction.rembedded_write_err_console;
+        }
+
+        @Override
+        public void execute(String x) {
+            call(x, x.length());
+        }
     }
 
     @Override
-    public String readConsole(String prompt) {
-        throw RInternalError.unimplemented();
+    public ReadConsoleNode createReadConsoleNode() {
+        return new TruffleNFI_ReadConsoleNode();
     }
 
     @Override
-    public void writeConsole(String x) {
-        throw RInternalError.unimplemented();
+    public WriteConsoleNode createWriteConsoleNode() {
+        return new TruffleNFI_WriteConsoleNode();
     }
 
     @Override
-    public void writeErrConsole(String x) {
-        throw RInternalError.unimplemented();
+    public WriteErrConsoleNode createWriteErrConsoleNode() {
+        return new TruffleNFI_WriteErrConsoleNode();
     }
 }
diff --git a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java
index a50310fc8e..9934ccd6fa 100644
--- a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java
+++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -143,7 +143,7 @@ public final class FFIProcessor extends AbstractProcessor {
         FileObject fileObj = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "com.oracle.truffle.r.ffi.impl.upcalls", "rffi_upcallsindex.h");
         note("If you edited any UpCallsRFFI interfaces do: cp " + fileObj.toUri().getPath() + " com.oracle.truffle.r.native/fficall/src/common\n");
         Writer w = fileObj.openWriter();
-        w.append("// GENERATED; DO NOT EDIT\n");
+        w.append("// GENERATED by com.oracle.truffle.r.ffi.processor.FFIProcessor class; DO NOT EDIT\n");
         w.append("#ifndef RFFI_UPCALLSINDEX_H\n");
         w.append("#define RFFI_UPCALLSINDEX_H\n");
         w.append('\n');
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java
new file mode 100644
index 0000000000..3bd3c753ad
--- /dev/null
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/DelegatingConsoleHandler.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018, 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.launcher;
+
+import java.util.function.Supplier;
+
+/**
+ * Represents a console handler that may delegate its operations to the given {@link ConsoleHandler}
+ * . This is used for R embedding where the user may provide custom overrides of some console
+ * functions.
+ */
+public abstract class DelegatingConsoleHandler extends ConsoleHandler {
+    public abstract void setDelegate(Supplier<ConsoleHandler> handler);
+}
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
index 7519a55c5b..136388102c 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, 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
@@ -35,6 +35,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Supplier;
 
 import org.graalvm.options.OptionCategory;
 import org.graalvm.polyglot.Context;
@@ -94,7 +95,7 @@ public class RCommand {
     public static RuntimeException fatal(String message, Object... args) {
         System.out.println("FATAL: " + String.format(message, args));
         System.exit(-1);
-        return null;
+        return new RuntimeException();
     }
 
     public static RuntimeException fatal(Throwable t, String message, Object... args) {
@@ -163,7 +164,7 @@ public class RCommand {
         }
         RCmdOptions options = RCmdOptions.parseArguments(Client.R, argsList.toArray(new String[argsList.size()]), false);
         assert env == null || env.length == 0 : "re-enable setting environments";
-        ConsoleHandler consoleHandler = createConsoleHandler(options, false, inStream, outStream);
+        ConsoleHandler consoleHandler = createConsoleHandler(options, null, inStream, outStream);
         try (Context context = Context.newBuilder().allowHostAccess(useJVM).options(polyglotOptions).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(
                         errStream).build()) {
             consoleHandler.setContext(context);
@@ -173,7 +174,7 @@ public class RCommand {
         }
     }
 
-    public static ConsoleHandler createConsoleHandler(RCmdOptions options, boolean embedded, InputStream inStream, OutputStream outStream) {
+    public static ConsoleHandler createConsoleHandler(RCmdOptions options, DelegatingConsoleHandler useDelegatingWrapper, InputStream inStream, OutputStream outStream) {
         /*
          * Whether the input is from stdin, a file (-f), or an expression on the command line (-e)
          * it goes through the console. N.B. -f and -e can't be used together and this is already
@@ -203,18 +204,20 @@ public class RCommand {
         } else {
             boolean isInteractive = options.getBoolean(RCmdOption.INTERACTIVE);
             if (!isInteractive && rsp.askForSave()) {
-                fatal("you must specify '--save', '--no-save' or '--vanilla'");
+                throw fatal("you must specify '--save', '--no-save' or '--vanilla'");
             }
-            if (embedded) {
+            boolean useReadLine = isInteractive && !rsp.noReadline();
+            if (useDelegatingWrapper != null) {
                 /*
                  * If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler
                  * should be lazy, as these may not be necessary and can cause hangs if stdin has
                  * been redirected.
                  */
-                throw fatal("embedded mode disabled");
-                // consoleHandler = new EmbeddedConsoleHandler(rsp, engine);
+                Supplier<ConsoleHandler> delegateFactory = useReadLine ? () -> new JLineConsoleHandler(inStream, outStream, rsp.isSlave())
+                                : () -> new DefaultConsoleHandler(inStream, outStream, isInteractive);
+                useDelegatingWrapper.setDelegate(delegateFactory);
+                return useDelegatingWrapper;
             } else {
-                boolean useReadLine = isInteractive && !rsp.noReadline();
                 if (useReadLine) {
                     return new JLineConsoleHandler(inStream, outStream, rsp.isSlave());
                 } else {
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java
index 82de6f3fab..4548d0e625 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RStartParams.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -47,23 +47,23 @@ import java.util.List;
  */
 public class RStartParams {
 
-    private final boolean quiet;
-    private final boolean slave;
+    private boolean quiet;
+    private boolean slave;
 
     /**
      * The setting of this value in GNU R is unusual and not simply based on the value of the
      * --interactive option, so we do not check the option in
      * {@link #RStartParams(RCmdOptions, boolean)}, but later in {@code RCommand}.
      */
-    private final boolean interactive;
-    private final boolean verbose;
-    private final boolean loadSiteFile;
-    private final boolean loadInitFile;
-    private final boolean debugInitFile;
+    private boolean interactive;
+    private boolean verbose;
+    private boolean loadSiteFile;
+    private boolean loadInitFile;
+    private boolean debugInitFile;
     private final boolean restoreAction;
     private final boolean askForSave;
     private final boolean save;
-    private final boolean noRenviron;
+    private boolean noRenviron;
 
     /**
      * This is not a configurable option, but it is set on the command line and needs to be stored
@@ -130,23 +130,19 @@ public class RStartParams {
     }
 
     /**
-     * Only upcalled from native code.
+     * Used for R embedding, allows to alter some of the values.
      */
-    @SuppressWarnings("unused")
-    public static void setParams(boolean quietA, boolean slaveA, boolean interactiveA, boolean verboseA, boolean loadSiteFileA,
+    public void setParams(boolean quietA, boolean slaveA, boolean interactiveA, boolean verboseA, boolean loadSiteFileA,
                     boolean loadInitFileA, boolean debugInitFileA, int restoreActionA, int saveActionA, boolean noRenvironA) {
-        assert false : "re-enable setParams";
-        // RStartParams params = RContext.getInstance().getStartParams();
-        // params.setQuiet(quietA);
-        // params.setSlave(slaveA);
-        // params.setInteractive(interactiveA);
-        // params.setVerbose(verboseA);
-        // params.setLoadSiteFile(loadSiteFileA);
-        // params.setLoadInitFile(loadInitFileA);
-        // params.setDebugInitFile(debugInitFileA);
-        // params.setSaveAction(SA_TYPE.values()[saveActionA]);
-        // params.setRestoreAction(SA_TYPE.values()[restoreActionA]);
-        // params.setNoRenviron(noRenvironA);
+        quiet = quietA;
+        slave = slaveA;
+        interactive = interactiveA;
+        verbose = verboseA;
+        loadSiteFile = loadSiteFileA;
+        loadInitFile = loadInitFileA;
+        debugInitFile = debugInitFileA;
+        // TODO: save and restore actions?
+        noRenviron = noRenvironA;
     }
 
     public boolean isQuiet() {
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
index 9fffb1b9ef..e4ca8fa47d 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, 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
@@ -136,7 +136,7 @@ public class RscriptCommand {
         RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, argsList.toArray(new String[argsList.size()]), false);
         String[] arguments = preprocessRScriptOptions(launcher, options);
 
-        ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, false, inStream, outStream);
+        ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, null, inStream, outStream);
         try (Context context = Context.newBuilder().allowHostAccess(useJVM).options(polyglotOptions).arguments("R", arguments).in(consoleHandler.createInputStream()).out(outStream).err(
                         errStream).build()) {
             consoleHandler.setContext(context);
diff --git a/com.oracle.truffle.r.native/fficall/Makefile b/com.oracle.truffle.r.native/fficall/Makefile
index 278d222f0c..f6176fd2d0 100644
--- a/com.oracle.truffle.r.native/fficall/Makefile
+++ b/com.oracle.truffle.r.native/fficall/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2018, 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
@@ -56,7 +56,7 @@ ifeq ($(FASTR_RFFI),managed)
 	# nop
 else
 ifeq ($(OS_NAME),Darwin)
-	$(DYLIB_LD) $(DYLIB_LDFLAGS) -Wl,-rpath,@loader_path/ -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -lRblas -lRlapack  -lpcre -lz $(VERSION_FLAGS)
+	$(DYLIB_LD) $(DYLIB_LDFLAGS) -Wl,-rpath,@loader_path/ -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -ldl -lRblas -lRlapack  -lpcre -lz $(VERSION_FLAGS)
 ifneq ($(FASTR_RFFI),llvm)	
 	install_name_tool -change libRblas.dylib @rpath/libRblas.dylib $(R_LIB)
 	install_name_tool -change libRlapack.dylib @rpath/libRlapack.dylib $(R_LIB)
@@ -65,7 +65,7 @@ ifneq ($(FASTR_RFFI),llvm)
 	mx rupdatelib $(FASTR_LIB_DIR)
 endif
 else
-	$(DYLIB_LD) $(DYLIB_LDFLAGS) $(shell echo $(PKG_LDFLAGS_OVERRIDE)) -Wl,-rpath,'$$ORIGIN' -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -lRblas -lRlapack  -lpcre -lz
+	$(DYLIB_LD) $(DYLIB_LDFLAGS) $(shell echo $(PKG_LDFLAGS_OVERRIDE)) -Wl,-rpath,'$$ORIGIN' -o $(R_LIB) $(wildcard lib/*.o) -L$(FASTR_LIB_DIR) -lRblas -lRlapack -ldl -lpcre -lz
 endif
 endif # managed
 
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 e0d64df093..b0eca857a1 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -319,4 +319,15 @@ typedef void (*call_R_ReleaseObject)(SEXP x);
 
 typedef void* (*call_R_alloc)(int n, int size);
 
+// IDEs and Tools
+typedef void* (*call_R_getGlobalFunctionContext)();
+typedef void* (*call_R_getParentFunctionContext)(void* c);
+typedef void* (*call_R_getContextEnv)(void* c);
+typedef void* (*call_R_getContextFun)(void* c);
+typedef void* (*call_R_getContextCall)(void* c);
+typedef void* (*call_R_getContextSrcRef)(void* c);
+typedef int (*call_R_insideBrowser)();
+typedef int (*call_R_isGlobal)(void* c);
+typedef int (*call_R_isEqual)(void* x, void* y);
+
 #endif
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c
deleted file mode 100644
index 8b96980452..0000000000
--- a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * 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-2015, The R Core Team
- * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-#include <dlfcn.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
-#include <rffiutils.h>
-#define R_INTERFACE_PTRS
-#include <R_ext/RStartup.h>
-#include <Rinterface.h>
-
-extern char **environ;
-
-static JavaVM *javaVM;
-static int initialized = 0;
-static char *java_home;
-
-static jclass rembeddedClass;
-static jclass rStartParamsClass;
-static jclass rInterfaceCallbacksClass;
-
-int R_running_as_main_program;
-int R_SignalHandlers;
-FILE * R_Consolefile;
-FILE * R_Outputfile;
-int R_DirtyImage; // TODO update this
-void *R_GlobalContext; // TODO what?
-SA_TYPE SaveAction; // ??
-
-typedef jint (JNICALL *JNI_CreateJavaVMFunc)
-	      (JavaVM **pvm, void **penv, void *args);
-
-
-static void *dlopen_jvmlib(char *libpath) {
-	void *handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW);
-	if (handle == NULL) {
-		fprintf(stderr, "Rf_initialize_R: cannot dlopen %s: %s\n", libpath, dlerror());
-		exit(1);
-	}
-	return handle;
-}
-
-// separate vm args from user args
-static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) {
-	int vcount = 0;
-	int ucount = 0;
-	for (int i = 0; i < argc; i++) {
-		char *arg = argv[i];
-		if ((arg[0] == '-' && arg[1] == 'X') || (arg[0] == '-' && arg[1] == 'D')) {
-			vmargv[vcount++] = arg;
-		} else {
-			uargv[ucount++] = arg;
-		}
-	}
-	return vcount;
-}
-
-static char **update_environ_with_java_home(void);
-static void print_environ(char **env);
-static char *get_classpath(char *r_home);
-
-# define JMP_BUF sigjmp_buf
-
-int Rf_initialize_R(int argc, char *argv[]) {
-	if (initialized) {
-		fprintf(stderr, "%s", "R is already initialized\n");
-		exit(1);
-	}
-	// print_environ(environ);
-	char *r_home = getenv("R_HOME");
-	if (r_home == NULL) {
-		fprintf(stderr, "R_HOME must be set\n");
-		exit(1);
-	}
-	struct utsname utsname;
-	uname(&utsname);
-	char jvmlib_path[256];
-	java_home = getenv("JAVA_HOME");
-	if (java_home == NULL) {
-		if (strcmp(utsname.sysname, "Linux") == 0) {
-			char *jvmdir = "/usr/java/latest";
-			struct stat statbuf;
-			if (stat(jvmdir, &statbuf) == 0) {
-				java_home = jvmdir;
-			}
-		} else if (strcmp(utsname.sysname, "Darwin") == 0) {
-			char *jvmdir = "/Library/Java/JavaVirtualMachines/jdk.latest";
-			struct stat statbuf;
-			if (stat(jvmdir, &statbuf) == 0) {
-				java_home = (char*)malloc(strlen(jvmdir) + 32);
-				strcpy(java_home, jvmdir);
-				strcat(java_home, "/Contents/Home");
-			}
-		}
-		if (java_home == NULL) {
-			fprintf(stderr, "Rf_initialize_R: can't find a JAVA_HOME\n");
-			exit(1);
-		}
-	}
-	strcpy(jvmlib_path, java_home);
-	if (strcmp(utsname.sysname, "Linux") == 0) {
-		strcat(jvmlib_path, "/jre/lib/amd64/server/libjvm.so");
-	} else if (strcmp(utsname.sysname, "Darwin") == 0) {
-		strcat(jvmlib_path, "/jre/lib/server/libjvm.dylib");
-        // Must also load libjli to avoid going through framework
-		// and failing to find our JAVA_HOME runtime
-		char jlilib_path[256];
-		strcpy(jlilib_path, java_home);
-		strcat(jlilib_path, "/jre/lib/jli/libjli.dylib");
-		dlopen_jvmlib(jlilib_path);
-	} else {
-		fprintf(stderr, "unsupported OS: %s\n", utsname.sysname);
-		exit(1);
-	}
-	void *vm_handle = dlopen_jvmlib(jvmlib_path);
-	JNI_CreateJavaVMFunc createJavaVMFunc = (JNI_CreateJavaVMFunc) dlsym(vm_handle, "JNI_CreateJavaVM");
-	if (createJavaVMFunc == NULL) {
-		fprintf(stderr, "Rf_initialize_R: cannot find JNI_CreateJavaVM\n");
-		exit(1);
-	}
-
-	char *vm_cp = get_classpath(r_home);
-	//printf("cp %s\n", vm_cp);
-
-	char **vmargs = malloc(argc * sizeof(char*));
-	char **uargs = malloc(argc * sizeof(char*));
-	int vmargc = process_vmargs(argc, argv, vmargs, uargs);
-	argc -= vmargc;
-	argv = uargs;
-	JavaVMOption vm_options[1 + vmargc];
-
-	vm_options[0].optionString = vm_cp;
-	for (int i = 0; i < vmargc; i++) {
-		vm_options[i + 1].optionString = vmargs[i];
-	}
-
-	JavaVMInitArgs vm_args;
-	vm_args.version = JNI_VERSION_1_8;
-	vm_args.nOptions = 1 + vmargc;
-	vm_args.options = vm_options;
-	vm_args.ignoreUnrecognized = JNI_TRUE;
-
-	JNIEnv *jniEnv;
-	jint flag = (*createJavaVMFunc)(&javaVM, (void**)
-			&jniEnv, &vm_args);
-	if (flag == JNI_ERR) {
-		fprintf(stderr, "Rf_initEmbeddedR: error creating Java VM, exiting...\n");
-		return 1;
-	}
-
-	setEmbedded();
-	setEnv(jniEnv);
-	rInterfaceCallbacksClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RInterfaceCallbacks");
-	rembeddedClass = checkFindClass(jniEnv, "com/oracle/truffle/r/engine/shell/REmbedded");
-	rStartParamsClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RStartParams");
-	jclass stringClass = checkFindClass(jniEnv, "java/lang/String");
-	jmethodID initializeMethod = checkGetMethodID(jniEnv, rembeddedClass, "initializeR", "([Ljava/lang/String;)V", 1);
-	jobjectArray argsArray = (*jniEnv)->NewObjectArray(jniEnv, argc, stringClass, NULL);
-	for (int i = 0; i < argc; i++) {
-		jstring arg = (*jniEnv)->NewStringUTF(jniEnv, argv[i]);
-		(*jniEnv)->SetObjectArrayElement(jniEnv, argsArray, i, arg);
-	}
-	// Can't TRACE this upcall as system not initialized
-	(*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray);
-	initialized++;
-	return 0;
-}
-
-char *R_HomeDir(void) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID R_HomeDirMethodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_HomeDir", "()Ljava/lang/String;", 0);
-	jstring homeDir = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, R_HomeDirMethodID);
-	const char *homeDirChars = stringToChars(jniEnv, homeDir);
-	return (char *)homeDirChars;
-}
-
-void R_SaveGlobalEnvToFile(const char *f) {
-	unimplemented("R_SaveGlobalEnvToFile");
-}
-
-void R_Suicide(const char *s) { ptr_R_Suicide(s); }
-
-#undef R_Interactive
-
-void R_DefParams(Rstart rs) {
-    // These are the GnuR defaults and correspond to the settings in RStartParams
-	// None of the size params make any sense for FastR
-    rs->R_Quiet = FALSE;
-    rs->R_Slave = FALSE;
-    rs->R_Interactive = TRUE;
-    rs->R_Verbose = FALSE;
-    rs->RestoreAction = SA_RESTORE;
-    rs->SaveAction = SA_SAVEASK;
-    rs->LoadSiteFile = TRUE;
-    rs->LoadInitFile = TRUE;
-    rs->DebugInitFile = FALSE;
-//    rs->vsize = R_VSIZE;
-//    rs->nsize = R_NSIZE;
-//    rs->max_vsize = R_SIZE_T_MAX;
-//    rs->max_nsize = R_SIZE_T_MAX;
-//    rs->ppsize = R_PPSSIZE;
-    rs->NoRenviron = FALSE;
-//    R_SizeFromEnv(Rp);
-}
-
-void R_SetParams(Rstart rs) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID setParamsMethodID = checkGetMethodID(jniEnv, rStartParamsClass, "setParams", "(ZZZZZZZIIZ)V", 1);
-	(*jniEnv)->CallStaticVoidMethod(jniEnv, rStartParamsClass, setParamsMethodID, rs->R_Quiet, rs->R_Slave, rs->R_Interactive,
-			rs->R_Verbose, rs->LoadSiteFile, rs->LoadInitFile, rs->DebugInitFile,
-			rs->RestoreAction, rs->SaveAction, rs->NoRenviron);
-}
-
-void R_SizeFromEnv(Rstart rs) {
-	unimplemented("R_SizeFromEnv");
-}
-
-void R_common_command_line(int *a, char **b, Rstart rs) {
-	unimplemented("R_common_command_line");
-}
-
-void R_set_command_line_arguments(int argc, char **argv) {
-	unimplemented("R_set_command_line_arguments");
-}
-
-
-int Rf_initEmbeddedR(int argc, char *argv[]) {
-	Rf_initialize_R(argc, argv);
-//	R_Interactive = TRUE;
-    setup_Rmainloop();
-    return 1;
-}
-
-void Rf_endEmbeddedR(int fatal) {
-	(*javaVM)->DestroyJavaVM(javaVM);
-	//TODO fatal
-}
-
-static void setupOverrides(void);
-
-void setup_Rmainloop(void) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID setupMethod = checkGetMethodID(jniEnv, rembeddedClass, "setupRmainloop", "()V", 1);
-	(*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, setupMethod);
-}
-
-void run_Rmainloop(void) {
-	JNIEnv *jniEnv = getEnv();
-	setupOverrides();
-	jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1);
-	(*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod);
-}
-
-void Rf_mainloop(void) {
-	setup_Rmainloop();
-	run_Rmainloop();
-}
-
-// functions that can be assigned by an embedded client to change behavior
-
-void uR_Suicide(const char *x) {
-	JNIEnv *jniEnv = getEnv();
-	jstring msg = (*jniEnv)->NewStringUTF(jniEnv, x);
-	jmethodID suicideMethod = checkGetMethodID(jniEnv, rembeddedClass, "R_Suicide", "(Ljava/lang/String;)V", 1);
-	(*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, suicideMethod, msg);
-}
-
-void uR_ShowMessage(const char *x) {
-	unimplemented("R_ShowMessage");
-}
-
-int uR_ReadConsole(const char *a, unsigned char *b, int c, int d) {
-	return (int) unimplemented("R_ReadConsole");
-}
-
-void uR_WriteConsole(const char *x, int y) {
-	unimplemented("R_WriteConsole");
-}
-
-void uR_WriteConsoleEx(const char *x, int y, int z) {
-	unimplemented("R_WriteConsole");
-}
-
-void uR_ResetConsole(void) {
-	unimplemented("R_ResetConsole");
-}
-
-void uR_FlushConsole(void) {
-	unimplemented("R_FlushConsole");
-}
-
-void uR_ClearerrConsole(void) {
-	unimplemented("R_ClearerrConsole");
-}
-
-void uR_Busy(int x) {
-	unimplemented("R_Busy");
-}
-
-void uR_CleanUp(SA_TYPE x, int y, int z) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_CleanUp", "(III)I", 1);
-	(*jniEnv)->CallStaticVoidMethod(jniEnv, UpCallsRFFIClass, methodID, x, y, z);
-}
-
-int uR_ShowFiles(int a, const char **b, const char **c,
-	       const char *d, Rboolean e, const char *f) {
-	return (int) unimplemented("R_ShowFiles");
-}
-
-int uR_ChooseFile(int a, char *b, int c) {
-	return (int) unimplemented("R_ChooseFile");
-}
-
-int uR_EditFile(const char *a) {
-	return (int) unimplemented("R_EditFile");
-}
-
-void uR_loadhistory(SEXP a, SEXP b, SEXP c, SEXP d) {
-	unimplemented("uR_loadhistory");
-}
-
-void uR_savehistory(SEXP a, SEXP b, SEXP c, SEXP d) {
-	unimplemented("R_savehistory");
-}
-
-void uR_addhistory(SEXP a, SEXP b, SEXP c, SEXP d) {
-	unimplemented("R_addhistory");
-}
-
-int  uR_EditFiles(int a, const char **b, const char **c, const char *d) {
-	return (int)unimplemented("");
-}
-
-SEXP udo_selectlist(SEXP a, SEXP b, SEXP c, SEXP d) {
-	return unimplemented("R_EditFiles");
-}
-
-SEXP udo_dataentry(SEXP a, SEXP b, SEXP c, SEXP d) {
-	return unimplemented("do_dataentry");
-}
-
-SEXP udo_dataviewer(SEXP a, SEXP b, SEXP c, SEXP d) {
-	return unimplemented("do_dataviewer");
-}
-
-void uR_ProcessEvents(void) {
-	unimplemented("R_ProcessEvents");
-}
-
-
-void (*ptr_R_Suicide)(const char *) = uR_Suicide;
-void (*ptr_R_ShowMessage)(const char *) = uR_ShowMessage;
-int  (*ptr_R_ReadConsole)(const char *, unsigned char *, int, int) = uR_ReadConsole;
-void (*ptr_R_WriteConsole)(const char *, int) = uR_WriteConsole;
-void (*ptr_R_WriteConsoleEx)(const char *, int, int) = uR_WriteConsoleEx;
-void (*ptr_R_ResetConsole)(void) = uR_ResetConsole;
-void (*ptr_R_FlushConsole)(void) = uR_FlushConsole;
-void (*ptr_R_ClearerrConsole)(void) = uR_ClearerrConsole;
-void (*ptr_R_Busy)(int) = uR_Busy;
-void (*ptr_R_CleanUp)(SA_TYPE, int, int) = uR_CleanUp;
-int  (*ptr_R_ShowFiles)(int, const char **, const char **,
-			       const char *, Rboolean, const char *) = uR_ShowFiles;
-int  (*ptr_R_ChooseFile)(int, char *, int) = uR_ChooseFile;
-int  (*ptr_R_EditFile)(const char *) = uR_EditFile;
-void (*ptr_R_loadhistory)(SEXP, SEXP, SEXP, SEXP) = uR_loadhistory;
-void (*ptr_R_savehistory)(SEXP, SEXP, SEXP, SEXP) = uR_savehistory;
-void (*ptr_R_addhistory)(SEXP, SEXP, SEXP, SEXP) = uR_addhistory;
-
-int  (*ptr_R_EditFiles)(int, const char **, const char **, const char *) = uR_EditFiles;
-
-SEXP (*ptr_do_selectlist)(SEXP, SEXP, SEXP, SEXP) = udo_selectlist;
-SEXP (*ptr_do_dataentry)(SEXP, SEXP, SEXP, SEXP) = udo_dataentry;
-SEXP (*ptr_do_dataviewer)(SEXP, SEXP, SEXP, SEXP) = udo_dataviewer;
-void (*ptr_R_ProcessEvents)() = uR_ProcessEvents;
-
-void setupOverrides(void) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID ovrMethodID = checkGetMethodID(jniEnv, rInterfaceCallbacksClass, "override", "(Ljava/lang/String;)V", 1);
-	jstring name;
-	if (ptr_R_Suicide != uR_Suicide) {
-		name = (*jniEnv)->NewStringUTF(jniEnv, "R_Suicide");
-		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
-	}
-	if (*ptr_R_CleanUp != uR_CleanUp) {
-		name = (*jniEnv)->NewStringUTF(jniEnv, "R_CleanUp");
-		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
-	}
-	if (*ptr_R_ReadConsole != uR_ReadConsole) {
-		name = (*jniEnv)->NewStringUTF(jniEnv, "R_ReadConsole");
-		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
-	}
-	if (*ptr_R_WriteConsole != uR_WriteConsole) {
-		name = (*jniEnv)->NewStringUTF(jniEnv, "R_WriteConsole");
-		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
-	}
-}
-
-static void REmbed_nativeWriteConsole(JNIEnv *jniEnv, jclass c, jstring string, int otype) {
-	jmp_buf error_jmpbuf;
-	callEnter(jniEnv, &error_jmpbuf);
-	if (!setjmp(error_jmpbuf)) {
-		int len = (*jniEnv)->GetStringUTFLength(jniEnv, string);
-		const char *cbuf =  (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL);
-		if (ptr_R_WriteConsole == NULL) {
-			(*ptr_R_WriteConsoleEx)(cbuf, len, otype);
-		} else {
-			(*ptr_R_WriteConsole)(cbuf, len);
-		}
-		(*jniEnv)->ReleaseStringUTFChars(jniEnv, string, cbuf);
-	}
-	callExit(jniEnv);
-}
-
-JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeWriteConsole(JNIEnv *jniEnv, jclass c, jstring string) {
-	REmbed_nativeWriteConsole(jniEnv, c, string, 0);
-}
-
-JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeWriteErrConsole(JNIEnv *jniEnv, jclass c, jstring string) {
-	REmbed_nativeWriteConsole(jniEnv, c, string, 1);
-}
-
-JNIEXPORT jstring JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeReadConsole(JNIEnv *jniEnv, jclass c, jstring prompt) {
-	jmp_buf error_jmpbuf;
-	jstring result = NULL;
-	callEnter(jniEnv, &error_jmpbuf);
-	if (!setjmp(error_jmpbuf)) {
-		const char *cprompt =  (*jniEnv)->GetStringUTFChars(jniEnv, prompt, NULL);
-		unsigned char cbuf[1024];
-		int n = (*ptr_R_ReadConsole)(cprompt, cbuf, 1024, 0);
-		result = (*jniEnv)->NewStringUTF(jniEnv, (const char *)cbuf);
-		(*jniEnv)->ReleaseStringUTFChars(jniEnv, prompt, cprompt);
-	}
-	callExit(jniEnv);
-	return result;
-}
-
-JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeCleanUp(JNIEnv *jniEnv, jclass c, jint x, jint y, jint z) {
-	jmp_buf error_jmpbuf;
-	callEnter(jniEnv, &error_jmpbuf);
-	if (!setjmp(error_jmpbuf)) {
-	(*ptr_R_CleanUp)(x, y, z);
-	}
-	callExit(jniEnv);
-}
-
-JNIEXPORT void JNICALL Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1REmbed_nativeSuicide(JNIEnv *jniEnv, jclass c, jstring string) {
-	jmp_buf error_jmpbuf;
-	callEnter(jniEnv, &error_jmpbuf);
-	if (!setjmp(error_jmpbuf)) {
-		const char *cbuf =  (*jniEnv)->GetStringUTFChars(jniEnv, string, NULL);
-		(*ptr_R_Suicide)(cbuf);
-	}
-	callExit(jniEnv);
-}
-
-void uR_PolledEvents(void) {
-	unimplemented("R_PolledEvents");
-}
-
-void (* R_PolledEvents)(void) = uR_PolledEvents;
-
-void Rf_jump_to_toplevel() {
-	unimplemented("Rf_jump_to_toplevel");
-}
-
-#include <R_ext/eventloop.h>
-
-fd_set *R_checkActivity(int usec, int ignore_stdin) {
-	return (fd_set*) unimplemented("R_checkActivity");
-}
-
-void R_runHandlers(InputHandler *handlers, fd_set *mask) {
-	unimplemented("R_runHandlers");
-}
-
-int R_wait_usec;
-
-#include <unistd.h>
-#include <errno.h>
-
-static void perror_exit(char *msg) {
-	perror(msg);
-	exit(1);
-}
-
-// support for getting the correct classpath for the VM
-// We use $R_HOME/bin/execRextras/Rclasspath to do this to emulate what happens
-// during normal execution
-static char *get_classpath(char *r_home) {
-	char **env = update_environ_with_java_home();
-	//print_environ(env);
-	int pipefd[2];
-	if (pipe(pipefd) == -1) {
-		perror_exit("pipe");
-	}
-	pid_t pid = fork();
-	if (pid == -1) {
-		perror("fork");
-	}
-	if (pid == 0) {
-		// child
-		char path[1024];
-		strcpy(path, r_home);
-		strcat(path, "/bin/execRextras/Rclasspath");
-		while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
-		close(pipefd[1]);
-		close(pipefd[0]);
-		int rc = execle(path, path, (char *)NULL, env);
-		if (rc == -1) {
-			perror_exit("exec");
-		}
-		return NULL;
-	} else {
-		// parent
-		const char *cpdef = "-Djava.class.path=";
-		char *buf = malloc(4096);
-		strcpy(buf, cpdef);
-		char *bufptr = buf + strlen(cpdef);
-		int max = 4096 - strlen(cpdef);
-		close(pipefd[1]);
-		while (1) {
-			int count = read(pipefd[0], bufptr, max);
-			if (count == -1) {
-				if (errno == EINTR) {
-					continue;
-				} else {
-					perror_exit("read");
-				}
-			} else if (count == 0) {
-			    // scrub any newline
-			    bufptr--;
-			    if (*bufptr != '\n') {
-			        bufptr++;
-			    }
-				*bufptr = 0;
-				break;
-			} else {
-				bufptr += count;
-				max -= count;
-			}
-		}
-		close(pipefd[0]);
-		wait(NULL);
-		return buf;
-	}
-}
-
-// debugging
-static void print_environ(char **env) {
-	fprintf(stdout, "## Environment variables at %p\n", env);
-	char **e = env;
-	while (*e != NULL) {
-		fprintf(stdout, "%s\n", *e);
-		e++;
-	}
-}
-
-static char **update_environ(char *def) {
-	int count = 0;
-	char **e = environ;
-	while (*e != NULL) {
-		e++;
-		count++;
-	}
-	char **new_env = malloc(sizeof(char *) * (count + 2));
-	e = environ;
-	char **ne = new_env;
-	while (*e != NULL) {
-		*ne++ = *e++;
-	}
-	*ne++ = def;
-	*ne = (char*) NULL;
-	return new_env;
-}
-
-static char **update_environ_with_java_home(void) {
-	char **e = environ;
-	while (*e != NULL) {
-		if (strstr(*e, "JAVA_HOME=")) {
-			return environ;
-		}
-		e++;
-	}
-	char *java_home_env = malloc(strlen(java_home) + 10);
-	strcpy(java_home_env, "JAVA_HOME=");
-	strcat(java_home_env, java_home);
-	return update_environ(java_home_env);
-}
-
-CTXT R_getGlobalFunctionContext() {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getGlobalFunctionContext", "()Ljava/lang/Object;", 0);
-    CTXT result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID);
-    SEXP new_result = checkRef(jniEnv, result);
-    return new_result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0);
-}
-
-CTXT R_getParentFunctionContext(CTXT c) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getParentFunctionContext", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-    CTXT result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, c);
-    SEXP new_result = checkRef(jniEnv, result);
-    return new_result == R_NilValue ? NULL : addGlobalRef(jniEnv, result, 0);
-}
-
-SEXP R_getContextEnv(CTXT context) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextEnv", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
-    return checkRef(jniEnv, result);
-}
-
-SEXP R_getContextFun(CTXT context) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextFun", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
-    return checkRef(jniEnv, result);
-}
-
-SEXP R_getContextCall(CTXT context) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextCall", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
-    return checkRef(jniEnv, result);
-}
-
-SEXP R_getContextSrcRef(CTXT context) {
-    JNIEnv *jniEnv = getEnv();
-    jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_getContextSrcRef", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-    SEXP result = (*jniEnv)->CallObjectMethod(jniEnv, UpCallsRFFIObject, methodID, context);
-    result = checkRef(jniEnv, result);
-    return result == R_NilValue ? NULL : result;
-}
-
-int R_insideBrowser() {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_insideBrowser", "()I", 0);
-    return (*jniEnv)->CallIntMethod(jniEnv, UpCallsRFFIObject, methodID);
-}
-
-int R_isGlobal(CTXT context) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_isGlobal", "(Ljava/lang/Object;)I", 0);
-    return (*jniEnv)->CallIntMethod(jniEnv, UpCallsRFFIObject, methodID, context);
-}
-
-int R_isEqual(void* x, void* y) {
-	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_isEqual", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
-    return (*jniEnv)->CallIntMethod(jniEnv, UpCallsRFFIObject, methodID, x, y);
-}
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile
index 1e6384f5d0..624b810ed0 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2018, 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
@@ -45,8 +45,9 @@ C_OBJECTS := $(LOCAL_C_OBJECTS) $(TRUFFLE_COMMON_C_OBJECTS)
 FFI_INCLUDES = -I$(TOPDIR)/include -I$(TOPDIR)/include/R_ext
 #NFI_INCLUDES is set in environment (by mx)
 LOCAL_INCLUDES = -I . -I $(abspath ../include) -I $(abspath ../common)
+JNI_INCLUDES = -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/$(JDK_OS_DIR)
 
-INCLUDES := $(LOCAL_INCLUDES) $(FFI_INCLUDES) $(NFI_INCLUDES)
+INCLUDES := $(LOCAL_INCLUDES) $(FFI_INCLUDES) $(NFI_INCLUDES) $(JNI_INCLUDES)
 
 # uncomment to see exactly where headers are being read from
 #CFLAGS := $(CFLAGS) -H
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c
index d611803139..147982d7a0 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c
@@ -1,30 +1,654 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 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
  *
- * 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.
+ * Copyright (c) 1995-2015, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates
  *
- * 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.
+ * All rights reserved.
  */
+
+#include <dlfcn.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#define R_INTERFACE_PTRS
 #include <Rinterface.h>
 #include <rffiutils.h>
+#include <R_ext/RStartup.h>
+#include <jni.h> 
 #include "../common/rffi_upcalls.h"
 
+// R_Interactive is actually field in the startup parameters structure, moreover, we'll also
+// set-up the value of the actual external global R_Interactive exported and supported
+// (in single threaded mode) by FastR.
+#undef R_Interactive
+
+extern char **environ;
+
+static JavaVM *javaVM;
+static JNIEnv *jniEnv = NULL;
+static int initialized = 0;
+static char *java_home;
+
+static jclass rembeddedClass;
+static jclass rStartParamsClass;
+static jclass rInterfaceCallbacksClass;
+
+int R_running_as_main_program;
+int R_SignalHandlers;
+FILE * R_Consolefile;
+FILE * R_Outputfile;
+int R_DirtyImage; // TODO update this
+void *R_GlobalContext; // TODO what?
+SA_TYPE SaveAction; // ??
+
+typedef jint (JNICALL *JNI_CreateJavaVMFunc)(JavaVM **pvm, void **penv, void *args);
+
+static void *dlopen_jvmlib(char *libpath) {
+	void *handle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW);
+	if (handle == NULL) {
+		fprintf(stderr, "Rf_initialize_R: cannot dlopen %s: %s\n", libpath, dlerror());
+		exit(1);
+	}
+	return handle;
+}
+
+// --------------------
+// JNI helpers
+
+static JNIEnv* getEnv() {
+    return jniEnv;
+}
+
+static jmethodID checkGetMethodID(JNIEnv *env, jclass klass, const char *name, const char *sig, int isStatic) {
+    jmethodID methodID = isStatic ? (*env)->GetStaticMethodID(env, klass, name, sig) : (*env)->GetMethodID(env, klass, name, sig);
+    if (methodID == NULL) {
+        char buf[1024];
+        strcpy(buf, "failed to find ");
+        strcat(buf, isStatic ? "static" : "instance");
+        strcat(buf, " method ");
+        strcat(buf, name);
+        strcat(buf, "(");
+        strcat(buf, sig);
+        strcat(buf, ")");
+        (*env)->FatalError(env, buf);
+    }
+    return methodID;
+}
+
+jclass checkFindClass(JNIEnv *env, const char *name) {
+    jclass klass = (*env)->FindClass(env, name);
+    if (klass == NULL) {
+        char buf[1024];
+        strcpy(buf, "failed to find class ");
+        strcat(buf, name);
+        strcat(buf, ".\nDid you set R_HOME to the correct location?");
+        (*env)->FatalError(env, buf);
+    }
+    return (*env)->NewGlobalRef(env, klass);
+}
+
+
+// ---------------------
+// UpCalls
+
+// IDE and tools up-calls
+
+CTXT R_getGlobalFunctionContext() {
+    return ((call_R_getGlobalFunctionContext) callbacks[R_getGlobalFunctionContext_x])();
+}
+
+CTXT R_getParentFunctionContext(CTXT c) {
+	return ((call_R_getParentFunctionContext) callbacks[R_getParentFunctionContext_x])(c);
+}
+
+SEXP R_getContextEnv(CTXT c) {
+	return ((call_R_getContextEnv) callbacks[R_getContextEnv_x])(c);
+}
+
+SEXP R_getContextFun(CTXT c) {
+	return ((call_R_getContextFun) callbacks[R_getContextFun_x])(c);
+}
+
+SEXP R_getContextCall(CTXT c) {
+	return ((call_R_getContextCall) callbacks[R_getContextCall_x])(c);
+}
+
+SEXP R_getContextSrcRef(CTXT c) {
+    return ((call_R_getContextSrcRef) callbacks[R_getContextSrcRef_x])(c);
+}
+
+int R_insideBrowser() {
+    return ((call_R_insideBrowser) callbacks[R_insideBrowser_x])();
+}
+
+int R_isGlobal(CTXT c) {
+    return ((call_R_isGlobal) callbacks[R_isGlobal_x])(c);
+}
+
+int R_isEqual(void* x, void* y) {
+	return ((call_R_isEqual) callbacks[R_isEqual_x])(x, y);
+}
+
+
+// separate vm args from user args
+static int process_vmargs(int argc, char *argv[], char *vmargv[], char *uargv[]) {
+	int vcount = 0;
+	int ucount = 0;
+	for (int i = 0; i < argc; i++) {
+		char *arg = argv[i];
+		if ((arg[0] == '-' && arg[1] == 'X') || (arg[0] == '-' && arg[1] == 'D')) {
+			vmargv[vcount++] = arg;
+		} else {
+			uargv[ucount++] = arg;
+		}
+	}
+	return vcount;
+}
+
+// Forward declarations of helper functions
+static char **update_environ_with_java_home(void);
+static void print_environ(char **env);
+static char *get_classpath(char *r_home);
+
+int Rf_initialize_R(int argc, char *argv[]) {
+	if (initialized) {
+		fprintf(stderr, "%s", "R is already initialized\n");
+		exit(1);
+	}
+	// print_environ(environ);
+	char *r_home = getenv("R_HOME");
+	if (r_home == NULL) {
+		fprintf(stderr, "R_HOME must be set\n");
+		exit(1);
+	}
+	struct utsname utsname;
+	uname(&utsname);
+	char jvmlib_path[256];
+	java_home = getenv("JAVA_HOME");
+	if (java_home == NULL) {
+		if (strcmp(utsname.sysname, "Linux") == 0) {
+			char *jvmdir = "/usr/java/latest";
+			struct stat statbuf;
+			if (stat(jvmdir, &statbuf) == 0) {
+				java_home = jvmdir;
+			}
+		} else if (strcmp(utsname.sysname, "Darwin") == 0) {
+			char *jvmdir = "/Library/Java/JavaVirtualMachines/jdk.latest";
+			struct stat statbuf;
+			if (stat(jvmdir, &statbuf) == 0) {
+				java_home = (char*)malloc(strlen(jvmdir) + 32);
+				strcpy(java_home, jvmdir);
+				strcat(java_home, "/Contents/Home");
+			}
+		}
+		if (java_home == NULL) {
+			fprintf(stderr, "Rf_initialize_R: can't find a JAVA_HOME\n");
+			exit(1);
+		}
+	}
+	strcpy(jvmlib_path, java_home);
+	if (strcmp(utsname.sysname, "Linux") == 0) {
+		strcat(jvmlib_path, "/jre/lib/amd64/server/libjvm.so");
+	} else if (strcmp(utsname.sysname, "Darwin") == 0) {
+		strcat(jvmlib_path, "/jre/lib/server/libjvm.dylib");
+        // Must also load libjli to avoid going through framework
+		// and failing to find our JAVA_HOME runtime
+		char jlilib_path[256];
+		strcpy(jlilib_path, java_home);
+		strcat(jlilib_path, "/jre/lib/jli/libjli.dylib");
+		dlopen_jvmlib(jlilib_path);
+	} else {
+		fprintf(stderr, "unsupported OS: %s\n", utsname.sysname);
+		exit(1);
+	}
+	void *vm_handle = dlopen_jvmlib(jvmlib_path);
+	JNI_CreateJavaVMFunc createJavaVMFunc = (JNI_CreateJavaVMFunc) dlsym(vm_handle, "JNI_CreateJavaVM");
+	if (createJavaVMFunc == NULL) {
+		fprintf(stderr, "Rf_initialize_R: cannot find JNI_CreateJavaVM\n");
+		exit(1);
+	}
+
+	char *vm_cp = get_classpath(r_home);
+	//printf("cp %s\n", vm_cp);
+
+	char **vmargs = malloc(argc * sizeof(char*));
+	char **uargs = malloc(argc * sizeof(char*));
+	int vmargc = process_vmargs(argc, argv, vmargs, uargs);
+	argc -= vmargc;
+	argv = uargs;
+	JavaVMOption vm_options[1 + vmargc];
+
+	vm_options[0].optionString = vm_cp;
+	for (int i = 0; i < vmargc; i++) {
+		vm_options[i + 1].optionString = vmargs[i];
+	}
+
+	JavaVMInitArgs vm_args;
+	vm_args.version = JNI_VERSION_1_8;
+	vm_args.nOptions = 1 + vmargc;
+	vm_args.options = vm_options;
+	vm_args.ignoreUnrecognized = JNI_TRUE;
+
+	jint flag = (*createJavaVMFunc)(&javaVM, (void**)
+			&jniEnv, &vm_args);
+	if (flag == JNI_ERR) {
+		fprintf(stderr, "Rf_initEmbeddedR: error creating Java VM, exiting...\n");
+		return 1;
+	}
+
+	rInterfaceCallbacksClass = checkFindClass(jniEnv, "com/oracle/truffle/r/runtime/RInterfaceCallbacks");
+	rembeddedClass = checkFindClass(jniEnv, "com/oracle/truffle/r/engine/shell/REmbedded");
+	jclass stringClass = checkFindClass(jniEnv, "java/lang/String");
+	jmethodID initializeMethod = checkGetMethodID(jniEnv, rembeddedClass, "initializeR", "([Ljava/lang/String;)V", 1);
+	jobjectArray argsArray = (*jniEnv)->NewObjectArray(jniEnv, argc, stringClass, NULL);
+	for (int i = 0; i < argc; i++) {
+		jstring arg = (*jniEnv)->NewStringUTF(jniEnv, argv[i]);
+		(*jniEnv)->SetObjectArrayElement(jniEnv, argsArray, i, arg);
+	}
+	// Can't TRACE this upcall as system not initialized
+	(*jniEnv)->CallStaticObjectMethod(jniEnv, rembeddedClass, initializeMethod, argsArray);
+	initialized++;
+	return 0;
+}
+
 char *R_HomeDir(void) {
-	return ((call_R_HomeDir) callbacks[R_HomeDir_x])();
+    // TODO: find out if this function could be invoked before the JVM and FastR get initialized
+    // in which case the access to callbacks will cause SIGSEGV
+    return ((call_R_HomeDir) callbacks[R_HomeDir_x])();
+}
+
+void R_SaveGlobalEnvToFile(const char *f) {
+	unimplemented("R_SaveGlobalEnvToFile");
+}
+
+void R_Suicide(const char *s) { ptr_R_Suicide(s); }
+
+void R_DefParams(Rstart rs) {
+    // These are the GnuR defaults and correspond to the settings in RStartParams
+	// None of the size params make any sense for FastR
+    rs->R_Quiet = FALSE;
+    rs->R_Slave = FALSE;
+    rs->R_Interactive = TRUE;
+    rs->R_Verbose = FALSE;
+    rs->RestoreAction = SA_RESTORE;
+    rs->SaveAction = SA_SAVEASK;
+    rs->LoadSiteFile = TRUE;
+    rs->LoadInitFile = TRUE;
+    rs->DebugInitFile = FALSE;
+//    rs->vsize = R_VSIZE;
+//    rs->nsize = R_NSIZE;
+//    rs->max_vsize = R_SIZE_T_MAX;
+//    rs->max_nsize = R_SIZE_T_MAX;
+//    rs->ppsize = R_PPSSIZE;
+    rs->NoRenviron = FALSE;
+//    R_SizeFromEnv(Rp);
+}
+
+// This call has to be made via JNI as we are not in a down-call, i.e. in truffle context, when this gets executed.
+void R_SetParams(Rstart rs) {
+	JNIEnv *jniEnv = getEnv();
+	jmethodID setParamsMethodID = checkGetMethodID(jniEnv, rembeddedClass, "setParams", "(ZZZZZZZIIZ)V", 1);
+	(*jniEnv)->CallStaticVoidMethod(jniEnv, rStartParamsClass, setParamsMethodID, rs->R_Quiet, rs->R_Slave, rs->R_Interactive,
+			rs->R_Verbose, rs->LoadSiteFile, rs->LoadInitFile, rs->DebugInitFile,
+			rs->RestoreAction, rs->SaveAction, rs->NoRenviron);
+}
+
+void R_SizeFromEnv(Rstart rs) {
+	unimplemented("R_SizeFromEnv");
+}
+
+void R_common_command_line(int *a, char **b, Rstart rs) {
+	unimplemented("R_common_command_line");
+}
+
+void R_set_command_line_arguments(int argc, char **argv) {
+	unimplemented("R_set_command_line_arguments");
+}
+
+
+int Rf_initEmbeddedR(int argc, char *argv[]) {
+	Rf_initialize_R(argc, argv);
+//	R_Interactive = TRUE;
+    setup_Rmainloop();
+    return 1;
+}
+
+void Rf_endEmbeddedR(int fatal) {
+	(*javaVM)->DestroyJavaVM(javaVM);
+	//TODO fatal
+}
+
+static void setupOverrides(void);
+
+void Rf_mainloop(void) {
+	JNIEnv *jniEnv = getEnv();
+	setupOverrides();
+	jmethodID mainloopMethod = checkGetMethodID(jniEnv, rembeddedClass, "runRmainloop", "()V", 1);
+	(*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, mainloopMethod);
+}
+
+// functions that can be assigned by an embedded client to change behavior
+
+// Note: pointer to this function is typically saved by the user to be called from that
+// user's R_Suicide override to actually really commit the suicide. We invoke this through
+// JNI intentionally to avoid any potential problems with NFI being called while destroying the VM.
+void uR_Suicide(const char *x) {
+	JNIEnv *jniEnv = getEnv();
+	jstring msg = (*jniEnv)->NewStringUTF(jniEnv, x);
+	jmethodID suicideMethod = checkGetMethodID(jniEnv, rembeddedClass, "R_Suicide", "(Ljava/lang/String;)V", 1);
+	(*jniEnv)->CallStaticVoidMethod(jniEnv, rembeddedClass, suicideMethod, msg);
+}
+
+void uR_ShowMessage(const char *x) {
+	unimplemented("R_ShowMessage");
+}
+
+int uR_ReadConsole(const char *a, unsigned char *b, int c, int d) {
+	return (int) unimplemented("R_ReadConsole");
 }
 
+void uR_WriteConsole(const char *x, int y) {
+	unimplemented("R_WriteConsole");
+}
+
+void uR_WriteConsoleEx(const char *x, int y, int z) {
+	unimplemented("R_WriteConsole");
+}
+
+void uR_ResetConsole(void) {
+	unimplemented("R_ResetConsole");
+}
+
+void uR_FlushConsole(void) {
+	unimplemented("R_FlushConsole");
+}
+
+void uR_ClearerrConsole(void) {
+	unimplemented("R_ClearerrConsole");
+}
+
+void uR_Busy(int x) {
+	unimplemented("R_Busy");
+}
+
+int uR_ShowFiles(int a, const char **b, const char **c,
+	       const char *d, Rboolean e, const char *f) {
+	return (int) unimplemented("R_ShowFiles");
+}
+
+int uR_ChooseFile(int a, char *b, int c) {
+	return (int) unimplemented("R_ChooseFile");
+}
+
+int uR_EditFile(const char *a) {
+	return (int) unimplemented("R_EditFile");
+}
+
+void uR_loadhistory(SEXP a, SEXP b, SEXP c, SEXP d) {
+	unimplemented("uR_loadhistory");
+}
+
+void uR_savehistory(SEXP a, SEXP b, SEXP c, SEXP d) {
+	unimplemented("R_savehistory");
+}
+
+void uR_addhistory(SEXP a, SEXP b, SEXP c, SEXP d) {
+	unimplemented("R_addhistory");
+}
+
+int  uR_EditFiles(int a, const char **b, const char **c, const char *d) {
+	return (int)unimplemented("");
+}
+
+SEXP udo_selectlist(SEXP a, SEXP b, SEXP c, SEXP d) {
+	return unimplemented("R_EditFiles");
+}
+
+SEXP udo_dataentry(SEXP a, SEXP b, SEXP c, SEXP d) {
+	return unimplemented("do_dataentry");
+}
+
+SEXP udo_dataviewer(SEXP a, SEXP b, SEXP c, SEXP d) {
+	return unimplemented("do_dataviewer");
+}
+
+void uR_ProcessEvents(void) {
+	unimplemented("R_ProcessEvents");
+}
+
+void uR_CleanUp(SA_TYPE x, int y, int z) {
+    return ((call_R_CleanUp) callbacks[R_CleanUp_x])(x, y, z);
+}
+
+void (*ptr_R_Suicide)(const char *) = uR_Suicide;
+void (*ptr_R_ShowMessage)(const char *) = uR_ShowMessage;
+int  (*ptr_R_ReadConsole)(const char *, unsigned char *, int, int) = uR_ReadConsole;
+void (*ptr_R_WriteConsole)(const char *, int) = uR_WriteConsole;
+void (*ptr_R_WriteConsoleEx)(const char *, int, int) = uR_WriteConsoleEx;
+void (*ptr_R_ResetConsole)(void) = uR_ResetConsole;
+void (*ptr_R_FlushConsole)(void) = uR_FlushConsole;
+void (*ptr_R_ClearerrConsole)(void) = uR_ClearerrConsole;
+void (*ptr_R_Busy)(int) = uR_Busy;
+void (*ptr_R_CleanUp)(SA_TYPE, int, int) = uR_CleanUp;
+int  (*ptr_R_ShowFiles)(int, const char **, const char **,
+			       const char *, Rboolean, const char *) = uR_ShowFiles;
+int  (*ptr_R_ChooseFile)(int, char *, int) = uR_ChooseFile;
+int  (*ptr_R_EditFile)(const char *) = uR_EditFile;
+void (*ptr_R_loadhistory)(SEXP, SEXP, SEXP, SEXP) = uR_loadhistory;
+void (*ptr_R_savehistory)(SEXP, SEXP, SEXP, SEXP) = uR_savehistory;
+void (*ptr_R_addhistory)(SEXP, SEXP, SEXP, SEXP) = uR_addhistory;
+
+int  (*ptr_R_EditFiles)(int, const char **, const char **, const char *) = uR_EditFiles;
+
+SEXP (*ptr_do_selectlist)(SEXP, SEXP, SEXP, SEXP) = udo_selectlist;
+SEXP (*ptr_do_dataentry)(SEXP, SEXP, SEXP, SEXP) = udo_dataentry;
+SEXP (*ptr_do_dataviewer)(SEXP, SEXP, SEXP, SEXP) = udo_dataviewer;
+void (*ptr_R_ProcessEvents)() = uR_ProcessEvents;
+
+// This call cannot be made via NFI because it is invoked from Rf_mainloop,
+// which is exported C function expected to be invoked by the embedded before actually starting R engine.
+void setupOverrides(void) {
+	JNIEnv *jniEnv = getEnv();
+	jmethodID ovrMethodID = checkGetMethodID(jniEnv, rInterfaceCallbacksClass, "override", "(Ljava/lang/String;)V", 1);
+	jstring name;
+	if (ptr_R_Suicide != uR_Suicide) {
+		name = (*jniEnv)->NewStringUTF(jniEnv, "R_Suicide");
+		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
+	}
+	if (*ptr_R_CleanUp != uR_CleanUp) {
+		name = (*jniEnv)->NewStringUTF(jniEnv, "R_CleanUp");
+		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
+	}
+	if (*ptr_R_ReadConsole != uR_ReadConsole) {
+		name = (*jniEnv)->NewStringUTF(jniEnv, "R_ReadConsole");
+		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
+	}
+	if (*ptr_R_WriteConsole != uR_WriteConsole) {
+		name = (*jniEnv)->NewStringUTF(jniEnv, "R_WriteConsole");
+		(*jniEnv)->CallStaticVoidMethod(jniEnv, rInterfaceCallbacksClass, ovrMethodID, name);
+	}
+}
+
+static void writeConsoleImpl(char *cbuf, int len, int otype) {
+    if (ptr_R_WriteConsole == NULL) {
+        // otype gives std (0) or err (1)
+        (*ptr_R_WriteConsoleEx)(cbuf, len, otype);
+    } else {
+        (*ptr_R_WriteConsole)(cbuf, len);
+    }
+}
+
+void uR_PolledEvents(void) {
+	unimplemented("R_PolledEvents");
+}
+
+void (* R_PolledEvents)(void) = uR_PolledEvents;
+
+void Rf_jump_to_toplevel() {
+	unimplemented("Rf_jump_to_toplevel");
+}
+
+#include <R_ext/eventloop.h>
+
+fd_set *R_checkActivity(int usec, int ignore_stdin) {
+	return (fd_set*) unimplemented("R_checkActivity");
+}
+
+void R_runHandlers(InputHandler *handlers, fd_set *mask) {
+	unimplemented("R_runHandlers");
+}
+
+// -----------------------------------------------------------------------------------------------
+// -----------------------------------------------------------------------------------------------
+// Downcalls from Java. We invoke these functions via .Call interface so that the callbacks
+// variable gets properly initialized and in general the R API is available. These functions
+// delegate to user provided C routines that may want to access the R API.
+// NOTE: those two functions are looked up by name!
+
+SEXP invokeCleanUp(SEXP x, SEXP y, SEXP z) {
+    ptr_R_CleanUp(Rf_asInteger(x), Rf_asInteger(y), Rf_asInteger(z));
+    return R_NilValue;
+}
+
+SEXP invokeSuicide(SEXP msg) {
+    ptr_R_Suicide(R_CHAR(STRING_ELT(msg, 0)));
+    return R_NilValue;
+}
+
+// TODO: these 3 are not yet invoked via .Call
+
+void rembedded_write_console(char *cbuf, int len) {
+    writeConsoleImpl(cbuf, len, 0);
+}
+
+void rembedded_write_err_console(char *cbuf, int len) {
+    writeConsoleImpl(cbuf, len, 1);
+}
+
+char* rembedded_read_console(const char* prompt) {
+    char* cbuf = malloc(sizeof(char) * 1024);
+    int n = (*ptr_R_ReadConsole)(prompt, cbuf, 1024, 0);
+    return cbuf;
+}
+
+// -----------------------------------------------------------------------------------------------
+// -----------------------------------------------------------------------------------------------
+// Helpers
+
+
+int R_wait_usec;    // TODO: necessary to resolve externals? otherwise dead code
+
+#include <unistd.h>
+#include <errno.h>
+
+static void perror_exit(char *msg) {
+	perror(msg);
+	exit(1);
+}
+
+// support for getting the correct classpath for the VM
+// We use $R_HOME/bin/execRextras/Rclasspath to do this to emulate what happens
+// during normal execution
+static char *get_classpath(char *r_home) {
+	char **env = update_environ_with_java_home();
+	//print_environ(env);
+	int pipefd[2];
+	if (pipe(pipefd) == -1) {
+		perror_exit("pipe");
+	}
+	pid_t pid = fork();
+	if (pid == -1) {
+		perror("fork");
+	}
+	if (pid == 0) {
+		// child
+		char path[1024];
+		strcpy(path, r_home);
+		strcat(path, "/bin/execRextras/Rclasspath");
+		while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+		close(pipefd[1]);
+		close(pipefd[0]);
+		int rc = execle(path, path, (char *)NULL, env);
+		if (rc == -1) {
+			perror_exit("exec");
+		}
+		return NULL;
+	} else {
+		// parent
+		const char *cpdef = "-Djava.class.path=";
+		char *buf = malloc(4096);
+		strcpy(buf, cpdef);
+		char *bufptr = buf + strlen(cpdef);
+		int max = 4096 - strlen(cpdef);
+		close(pipefd[1]);
+		while (1) {
+			int count = read(pipefd[0], bufptr, max);
+			if (count == -1) {
+				if (errno == EINTR) {
+					continue;
+				} else {
+					perror_exit("read");
+				}
+			} else if (count == 0) {
+			    // scrub any newline
+			    bufptr--;
+			    if (*bufptr != '\n') {
+			        bufptr++;
+			    }
+				*bufptr = 0;
+				break;
+			} else {
+				bufptr += count;
+				max -= count;
+			}
+		}
+		close(pipefd[0]);
+		wait(NULL);
+		return buf;
+	}
+}
+
+static char **update_environ(char *def) {
+	int count = 0;
+	char **e = environ;
+	while (*e != NULL) {
+		e++;
+		count++;
+	}
+	char **new_env = malloc(sizeof(char *) * (count + 2));
+	e = environ;
+	char **ne = new_env;
+	while (*e != NULL) {
+		*ne++ = *e++;
+	}
+	*ne++ = def;
+	*ne = (char*) NULL;
+	return new_env;
+}
+
+static char **update_environ_with_java_home(void) {
+	char **e = environ;
+	while (*e != NULL) {
+		if (strstr(*e, "JAVA_HOME=")) {
+			return environ;
+		}
+		e++;
+	}
+	char *java_home_env = malloc(strlen(java_home) + 10);
+	strcpy(java_home_env, "JAVA_HOME=");
+	strcat(java_home_env, java_home);
+	return update_environ(java_home_env);
+}
+
+// debugging
+static void print_environ(char **env) {
+	fprintf(stdout, "## Environment variables at %p\n", env);
+	char **e = env;
+	while (*e != NULL) {
+		fprintf(stdout, "%s\n", *e);
+		e++;
+	}
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
index 2d774b7c73..58acd5b4ae 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, 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
@@ -44,7 +44,7 @@ import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
-import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
@@ -89,7 +89,7 @@ public final class RBuiltinPackages implements RBuiltinLookup {
                     baseEnv.put(methodName, function);
                     baseEnv.lockBinding(methodName);
                 } catch (PutException ex) {
-                    Utils.rSuicide("failed to install builtin function: " + methodName);
+                    RSuicide.rSuicide("failed to install builtin function: " + methodName);
                 }
             }
         }
@@ -100,7 +100,7 @@ public final class RBuiltinPackages implements RBuiltinLookup {
         try {
             baseSource = RSource.fromFileName(basePathbase.toString(), true);
         } catch (IOException ex) {
-            throw Utils.rSuicide(String.format("unable to open the base package %s", basePathbase));
+            throw RSuicide.rSuicide(String.format("unable to open the base package %s", basePathbase));
         }
         // Load the (stub) DLL for base
         if (FastROptions.LoadPackagesNativeCode.getBooleanValue()) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
index 015e798f80..d7627a2652 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
@@ -6,7 +6,7 @@
  * 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) 2013, 2017, Oracle and/or its affiliates
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -68,7 +68,7 @@ public abstract class Quit extends RBuiltinNode.Arg3 {
             warning(RError.Message.QUIT_INVALID_RUNLAST);
             runLast = RRuntime.LOGICAL_FALSE;
         }
-        RCleanUp.cleanUp(ask, status, RRuntime.fromLogical(runLast));
+        RCleanUp.cleanUp(RContext.getInstance(), ask, status, RRuntime.fromLogical(runLast));
         throw RInternalError.shouldNotReachHere("cleanup returned");
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java
index a384de71c6..1c1f2649c4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.runtime.REnvVars;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 
@@ -42,7 +43,7 @@ public abstract class SystemFunctionFactory {
             theInstance = (SystemFunctionFactory) Class.forName(className).newInstance();
         } catch (Exception ex) {
             // CheckStyle: stop system..print check
-            Utils.rSuicide("Failed to instantiate class: " + className);
+            RSuicide.rSuicide("Failed to instantiate class: " + className);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
index 633f882eff..2bfa09ae8d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
@@ -40,7 +40,7 @@ import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -286,7 +286,7 @@ public class TraceHandling {
             try {
                 fileWriter = new FileWriter("fastr_tracecalls.log");
             } catch (IOException e) {
-                Utils.rSuicide("failed to open 'fastr_tracecalls.log'" + e.getMessage());
+                RSuicide.rSuicide("failed to open 'fastr_tracecalls.log'" + e.getMessage());
             }
         }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java
index 800c076625..aa6ee73ec4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/LazyResourceHandlerFactory.java
@@ -37,6 +37,7 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
 import com.oracle.truffle.r.runtime.ResourceHandlerFactory.Handler;
+import com.oracle.truffle.r.runtime.context.RContext;
 
 /**
  * Default implementation uses the default mechanism in {@code java.lang.Class}.
@@ -105,7 +106,7 @@ class LazyResourceHandlerFactory extends ResourceHandlerFactory implements Handl
             }
             return result;
         } catch (Exception ex) {
-            Utils.rSuicide(ex, "Could not load R files from resources. Details: " + ex.getMessage());
+            RSuicide.rSuicide(RContext.getInstance(), ex, "Could not load R files from resources. Details: " + ex.getMessage());
             return null;
         }
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
index 37065d16a1..4d5f97ecf4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1995, 1996  Robert Gentleman and Ross Ihaka
  * Copyright (c) 1997-2014,  The R Core Team
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -13,14 +13,27 @@ package com.oracle.truffle.r.runtime;
 
 import java.util.ArrayList;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.r.launcher.RStartParams;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ConsoleIO;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.ffi.CallRFFI.InvokeCallNode;
+import com.oracle.truffle.r.runtime.ffi.DLL.RFindSymbolNode;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.gnur.SA_TYPE;
 import com.oracle.truffle.r.runtime.instrument.InstrumentationState;
 
-public class RCleanUp {
+public abstract class RCleanUp {
+
+    private RCleanUp() {
+    }
 
     private static ArrayList<InstrumentationState.CleanupHandler> cleanupHandlers = new ArrayList<>();
 
@@ -28,9 +41,11 @@ public class RCleanUp {
         cleanupHandlers.add(cleanupHandler);
     }
 
-    public static void cleanUp(SA_TYPE saveType, int status, boolean runLast) {
+    public static void cleanUp(RContext ctx, SA_TYPE saveType, int status, boolean runLast) {
         if (RInterfaceCallbacks.R_CleanUp.isOverridden()) {
-            RFFIFactory.getREmbedRFFI().cleanUp(saveType.ordinal(), status, runLast ? 1 : 0);
+            RootCallTarget invokeUserCleanup = ctx.getOrCreateCachedCallTarget(UserDefinedCleanUpRootNode.class, () -> new UserDefinedCleanUpRootNode(ctx).getCallTarget());
+            Object[] args = new Object[]{asVector(saveType.ordinal()), asVector(status), asVector(runLast ? 1 : 0)};
+            invokeUserCleanup.call(args);
         } else {
             stdCleanUp(saveType, status, runLast);
         }
@@ -118,4 +133,31 @@ public class RCleanUp {
         // TODO errors should return to toplevel if interactive
         RContext.getEngine().checkAndRunStartupShutdownFunction(".Last.sys");
     }
+
+    private static RIntVector asVector(int value) {
+        return RDataFactory.createIntVectorFromScalar(value);
+    }
+
+    private static final class UserDefinedCleanUpRootNode extends RootNode {
+        protected UserDefinedCleanUpRootNode(RContext ctx) {
+            super(null);
+            invokeCallNode = ctx.getRFFI().callRFFI.createInvokeCallNode();
+            Truffle.getRuntime().createCallTarget(this);
+        }
+
+        private static final String SYMBOL_NAME = "invokeCleanUp";
+        @Child InvokeCallNode invokeCallNode;
+        @Child RFindSymbolNode findSymbolNode = RFindSymbolNode.create();
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            SymbolHandle invokeCleanUp = findSymbolNode.execute(SYMBOL_NAME, null, null);
+            if (invokeCleanUp == null) {
+                CompilerDirectives.transferToInterpreter();
+                throw RInternalError.shouldNotReachHere("Cannot find " + SYMBOL_NAME + " symbol which should be declared in Rembedded.c");
+            }
+            invokeCallNode.dispatch(new NativeCallInfo(SYMBOL_NAME, invokeCleanUp, null), frame.getArguments());
+            return null;
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
index acd462de0f..c9103b3d8c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
@@ -175,7 +175,7 @@ public final class REnvVars implements RContext.ContextState {
                 rHomePath = Paths.get(rHome);
             }
             if (!validateRHome(rHomePath, markerFile())) {
-                Utils.rSuicide("R_HOME is not set correctly");
+                RSuicide.rSuicide("R_HOME is not set correctly");
             }
             rHome = rHomePath.toString();
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 6adae8c519..a0282f634e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -273,7 +273,7 @@ public final class RError extends RuntimeException implements TruffleException {
 
     /**
      * A temporary error that indicates an unimplemented feature where terminating the VM using
-     * {@link Utils#rSuicide(String)} would be inappropriate.
+     * {@link RSuicide#rSuicide(String)} would be inappropriate.
      */
     public static RError nyi(RBaseNode node, String msg) {
         CompilerDirectives.transferToInterpreter();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java
index 6a1b49c4a5..0ce6fde2ef 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInterfaceCallbacks.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -23,7 +23,10 @@
 package com.oracle.truffle.r.runtime;
 
 /**
- * Support for callbacks in embedded mode for certain VM operations.
+ * Support for callbacks in embedded mode for certain VM operations. If the embedding code overrides
+ * certain operation, the flag for that operation is updated here and when FastR is about to invoke
+ * one of those operations, normally implemented by default by FastR in Java, it checks the flag and
+ * eventually down-calls to the user provided handler.
  *
  */
 public enum RInterfaceCallbacks {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
index 1c6a328bec..e1b9a36110 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -198,7 +198,7 @@ public final class RInternalError extends Error implements TruffleException {
                     }
                     System.err.println(message + " (see fastr_errors.log" + suffix + ")");
                     if (RContext.isEmbedded()) {
-                        Utils.rSuicide("FastR internal error");
+                        RSuicide.rSuicide("FastR internal error");
                     }
                 }
                 if (!FastROptions.PrintErrorStacktraces.getBooleanValue() && !FastROptions.PrintErrorStacktracesToFile.getBooleanValue()) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java
index eca6c3fc56..1ee08bc2ca 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -49,7 +49,7 @@ public class RPlatform {
                 default:
                     osSubDir = null;
                     libExt = null;
-                    Utils.rSuicide("CallRFFI: unsupported OS: " + osName);
+                    RSuicide.rSuicide("CallRFFI: unsupported OS: " + osName);
             }
         }
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
index 9c7682b523..f726727a4a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, 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
@@ -96,7 +96,7 @@ public final class RProfile implements RContext.ContextState {
         Path path = FileSystems.getDefault().getPath(REnvVars.rHome(), "library", "base", "R", "Rprofile");
         Source source = getProfile(path.toString(), true);
         if (source == null) {
-            Utils.rSuicide("can't find system profile");
+            RSuicide.rSuicide("can't find system profile");
         }
         return source;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java
new file mode 100644
index 0000000000..632e7b2594
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSuicide.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.ffi.CallRFFI.InvokeCallNode;
+import com.oracle.truffle.r.runtime.ffi.DLL.RFindSymbolNode;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+
+public abstract class RSuicide {
+    private RSuicide() {
+    }
+
+    /**
+     * Please use {@link #rSuicide(RContext, String)} overload whenever the context is available.
+     */
+    public static RuntimeException rSuicide(String msg) {
+        throw rSuicide(RContext.getInstance(), msg);
+    }
+
+    /**
+     * Called when the system encounters a fatal internal error and must commit suicide (i.e.
+     * terminate). It allows an embedded client to override the default (although they typically
+     * invoke the default eventually).
+     */
+    public static RuntimeException rSuicide(RContext ctx, String msg) {
+        invokeUserDefinedSuicide(ctx, msg);
+        throw rSuicideDefault(msg);
+    }
+
+    public static RuntimeException rSuicide(RContext ctx, Throwable cause, String msg) {
+        invokeUserDefinedSuicide(ctx, msg);
+        throw rSuicideDefault(msg);
+    }
+
+    /**
+     * The default, non-overrideable, suicide call. It prints the message and throws
+     * {@link ExitException}.
+     *
+     * @param msg
+     */
+    public static RuntimeException rSuicideDefault(String msg) {
+        System.err.println("FastR unexpected failure: " + msg);
+        throw new ExitException(2, false);
+    }
+
+    private static RStringVector asVector(String value) {
+        return RDataFactory.createStringVector(value);
+    }
+
+    private static void invokeUserDefinedSuicide(RContext ctx, String msg) {
+        if (ctx != null && RInterfaceCallbacks.R_Suicide.isOverridden()) {
+            RootCallTarget invokeUserCleanup = ctx.getOrCreateCachedCallTarget(UserDefinedSuicideRootNode.class, () -> new UserDefinedSuicideRootNode(ctx).getCallTarget());
+            invokeUserCleanup.call(new Object[]{asVector(msg)});
+        }
+    }
+
+    private static final class UserDefinedSuicideRootNode extends RootNode {
+        protected UserDefinedSuicideRootNode(RContext ctx) {
+            super(null);
+            invokeCallNode = ctx.getRFFI().callRFFI.createInvokeCallNode();
+            Truffle.getRuntime().createCallTarget(this);
+        }
+
+        private static final String SYMBOL_NAME = "invokeSuicide";
+        @Child InvokeCallNode invokeCallNode;
+        @Child RFindSymbolNode findSymbolNode = RFindSymbolNode.create();
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            SymbolHandle invokeSuicide = findSymbolNode.execute(SYMBOL_NAME, null, null);
+            if (invokeSuicide == null) {
+                CompilerDirectives.transferToInterpreter();
+                throw RInternalError.shouldNotReachHere("Cannot find " + SYMBOL_NAME + " symbol which should be declared in Rembedded.c");
+            }
+            invokeCallNode.dispatch(new NativeCallInfo(SYMBOL_NAME, invokeSuicide, null), frame.getArguments());
+            return null;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
index fc28f3a8eb..e6775d5e3d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -72,7 +72,7 @@ public class TempPathName implements RContext.ContextState {
         if (t != null) {
             tempDirPath = t;
         } else {
-            Utils.rSuicide("cannot create 'R_TempDir'");
+            RSuicide.rSuicide("cannot create 'R_TempDir'");
         }
 
         // initialize deparse directory
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 18e5348b3e..9f542e9f7a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -60,7 +60,6 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.MultiSlotData;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 public final class Utils {
@@ -139,7 +138,7 @@ public final class Utils {
             } catch (IOException ex) {
             }
         }
-        throw Utils.rSuicide("resource " + resourceName + " not found");
+        throw RSuicide.rSuicide("resource " + resourceName + " not found");
     }
 
     private static String getResourceAsString(InputStream is) throws IOException {
@@ -170,37 +169,6 @@ public final class Utils {
 
     }
 
-    /**
-     * Called when the system encounters a fatal internal error and must commit suicide (i.e.
-     * terminate). It allows an embedded client to override the default (although they typically
-     * invoke the default eventually).
-     */
-    public static RuntimeException rSuicide(String msg) {
-        if (RInterfaceCallbacks.R_Suicide.isOverridden()) {
-            RFFIFactory.getREmbedRFFI().suicide(msg);
-        }
-        throw rSuicideDefault(msg);
-    }
-
-    public static RuntimeException rSuicide(Throwable cause, String msg) {
-        cause.printStackTrace();
-        if (RInterfaceCallbacks.R_Suicide.isOverridden()) {
-            RFFIFactory.getREmbedRFFI().suicide(msg);
-        }
-        throw rSuicideDefault(msg);
-    }
-
-    /**
-     * The default, non-overrideable, suicide call. It prints the message and throws
-     * {@link ExitException}.
-     *
-     * @param msg
-     */
-    public static RuntimeException rSuicideDefault(String msg) {
-        System.err.println("FastR unexpected failure: " + msg);
-        throw new ExitException(2, false);
-    }
-
     /**
      * This the real, final, non-overrideable, exit of the entire R system. TODO well, modulo how
      * quit() is interpreted when R is started implicitly from a Polyglot shell that is running
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
index 9925131aad..d8b16e8530 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
@@ -32,7 +32,7 @@ import java.util.EnumSet;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -70,7 +70,7 @@ public class StdConnections {
                 stdout = new StdoutConnection(console);
                 stderr = new StderrConnection(console);
             } catch (IOException ex) {
-                throw Utils.rSuicide("failed to open stdconnections:");
+                throw RSuicide.rSuicide("failed to open stdconnections:");
             }
             return this;
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
index 32908201d1..6f6fc75678 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
@@ -44,8 +44,8 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.RErrorException;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.VirtualEvalFrame;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
@@ -858,7 +858,7 @@ public abstract class REnvironment extends RAttributeStorage {
         try {
             put(key, value);
         } catch (PutException ex) {
-            Utils.rSuicide("exception in safePut");
+            RSuicide.rSuicide("exception in safePut");
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
index 8bc97e9541..07b6aeba98 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
@@ -32,6 +32,7 @@ import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RError.RErrorException;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -432,11 +433,20 @@ public class DLL {
     /**
      * Loads a the {@code libR} library. This is an implementation specific library.
      */
-    public static void loadLibR(String path) {
-        RContext context = RContext.getInstance();
-        Object handle = DLLRFFI.DLOpenRootNode.create(context).call(path, false, false);
+    public static void loadLibR(RContext context, String path) {
+        Object handle = null;
+        try {
+            handle = DLLRFFI.DLOpenRootNode.create(context).call(path, false, false);
+        } catch (UnsatisfiedLinkError ex) {
+            throw RSuicide.rSuicide(context, "error loading libR from: " + path + ".\n" +
+                            "If running on NFI backend, did you provide location of libtrufflenfi.so as value of system " +
+                            "property 'truffle.nfi.library'?\nThe current value is '" +
+                            System.getProperty("truffle.nfi.library") + "'. \nDetails: " + ex.getMessage());
+        } catch (Throwable ex) {
+            throw RSuicide.rSuicide(context, "error loading libR from: " + path + ". Details: " + ex.getMessage());
+        }
         if (handle == null) {
-            throw Utils.rSuicide("error loading libR from: " + path + "\n");
+            throw RSuicide.rSuicide(context, "error loading libR from: " + path + "\n");
         }
         ContextStateImpl dllContext = context.stateDLL;
         dllContext.addLibR(DLLInfo.create(libName(path), path, true, handle, false));
@@ -493,7 +503,7 @@ public class DLL {
                     if (RContext.isInitialContextInitialized()) {
                         throw new DLLException(ex, RError.Message.DLL_RINIT_ERROR);
                     } else {
-                        throw Utils.rSuicide(ex, RError.Message.DLL_RINIT_ERROR.message + " on default package: " + path);
+                        throw RSuicide.rSuicide(RContext.getInstance(), ex, RError.Message.DLL_RINIT_ERROR.message + " on default package: " + path);
                     }
                 }
             } catch (UnsatisfiedLinkError ex) {
@@ -506,8 +516,8 @@ public class DLL {
          * There is no sense in throwing an RError if we fail to load/init a (default) package
          * during initial context initialization, as it is essentially fatal for any of the standard
          * packages and likely indicates a bug in the RFFI implementation. So we call
-         * {@link Utils#rSuicide(String)} instead. When the system is stable, we can undo this, so
-         * that errors loading (user) packages added to R_DEFAULT_PACKAGES do throw RErrors.
+         * {@link RSuicide#rSuicide(String)} instead. When the system is stable, we can undo this,
+         * so that errors loading (user) packages added to R_DEFAULT_PACKAGES do throw RErrors.
          */
         private synchronized DLLInfo doLoad(String absPath, boolean local, boolean now, boolean addToList) throws DLLException {
             RFFIContext stateRFFI = RContext.getInstance().getStateRFFI();
@@ -521,7 +531,7 @@ public class DLL {
                 if (RContext.isInitialContextInitialized()) {
                     throw new DLLException(ex, RError.Message.DLL_LOAD_ERROR, absPath, dlError);
                 } else {
-                    throw Utils.rSuicide(ex, "error loading default package: " + absPath + "\n" + dlError);
+                    throw RSuicide.rSuicide(RContext.getInstance(), ex, "error loading default package: " + absPath + "\n" + dlError);
                 }
             } finally {
                 stateRFFI.afterDowncall(before);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java
index 0534eaa651..f30a5dfbf4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java
@@ -83,6 +83,12 @@ public enum NativeFunction {
     lminfl("([double], sint32, sint32, sint32, sint32, [double], [double], [double], [double], [double], double): void", "call_stats_", "stats"),
     // FastR helpers
     set_exception_flag("(): void"),
+    // FastR internal helper for R embedded mode
+    rembedded_write_console("(string, sint32):void"),
+    rembedded_write_err_console("(string, sint32):void"),
+    rembedded_read_console("(string):string"),
+    rembedded_native_clean_up("(sint32, sint32, sint32):void"),
+    rembedded_native_suicide("(string):void"),
     // user-defined RNG
     unif_init("(sint32): void", "user_", anyLibrary()),
     norm_rand("(): pointer", "user_", anyLibrary()),
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java
index 0be0b85c6a..929f2558b4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/REmbedRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -22,18 +22,40 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.nodes.NodeInterface;
+
 /**
- * Function downcalls related to the embedded API.
+ * Function down-calls related to the embedded API. TODO: these all should be invoked as proper
+ * down-calls because the user code may want to use R API.
  */
 public interface REmbedRFFI {
-    void suicide(String x);
+    interface ReadConsoleNode extends NodeInterface {
+        String execute(String prompt);
+
+        static REmbedRFFI.ReadConsoleNode create() {
+            return RFFIFactory.getREmbedRFFI().createReadConsoleNode();
+        }
+    }
+
+    interface WriteConsoleBaseNode extends NodeInterface {
+        void execute(String x);
+    }
 
-    void cleanUp(int type, int x, int y);
+    interface WriteConsoleNode extends WriteConsoleBaseNode {
+        static REmbedRFFI.WriteConsoleNode create() {
+            return RFFIFactory.getREmbedRFFI().createWriteConsoleNode();
+        }
+    }
 
-    String readConsole(String prompt);
+    interface WriteErrConsoleNode extends WriteConsoleBaseNode {
+        static REmbedRFFI.WriteErrConsoleNode create() {
+            return RFFIFactory.getREmbedRFFI().createWriteErrConsoleNode();
+        }
+    }
 
-    void writeConsole(String x);
+    ReadConsoleNode createReadConsoleNode();
 
-    void writeErrConsole(String x);
+    WriteConsoleNode createWriteConsoleNode();
 
+    WriteErrConsoleNode createWriteErrConsoleNode();
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index dcd77427f7..a09f842578 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -23,7 +23,7 @@
 package com.oracle.truffle.r.runtime.ffi;
 
 import com.oracle.truffle.r.runtime.FastRConfig;
-import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 
@@ -71,7 +71,7 @@ public abstract class RFFIFactory {
         try {
             rffiFactory = (RFFIFactory) Class.forName(klassName).newInstance();
         } catch (Exception ex) {
-            throw Utils.rSuicide("Failed to instantiate class: " + klassName + ": " + ex);
+            throw RSuicide.rSuicide("Failed to instantiate class: " + klassName + ": " + ex);
         }
     }
 
@@ -83,7 +83,7 @@ public abstract class RFFIFactory {
         try {
             return Type.valueOf(prop.toUpperCase());
         } catch (IllegalArgumentException ex) {
-            throw Utils.rSuicide("No RFFI factory: " + prop);
+            throw RSuicide.rSuicide("No RFFI factory: " + prop);
         }
     }
 
diff --git a/com.oracle.truffle.r.test.native/embedded/src/main.c b/com.oracle.truffle.r.test.native/embedded/src/main.c
index 9a01de741f..9de0840477 100644
--- a/com.oracle.truffle.r.test.native/embedded/src/main.c
+++ b/com.oracle.truffle.r.test.native/embedded/src/main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -70,11 +70,13 @@ int main(int argc, char **argv) {
 		printf("R_HOME must be set\n");
 		exit(1);
 	}
+	printf("Initializing R with Rf_initialize_R...\n");
 	Rf_initialize_R(argc, argv);
 	structRstart rp;
 	Rstart Rp = &rp;
 	R_DefParams(Rp);
 	Rp->SaveAction = SA_SAVEASK;
+	printf("Initializing R with R_SetParams...\n");
 	R_SetParams(Rp);
 	ptr_stdR_CleanUp = ptr_R_CleanUp;
 	ptr_R_CleanUp = &testR_CleanUp;
@@ -82,7 +84,12 @@ int main(int argc, char **argv) {
 	ptr_R_Suicide = &testR_Suicide;
 	ptr_R_ReadConsole = &testR_ReadConsole;
 	ptr_R_WriteConsole = &testR_WriteConsole;
-	DllInfo *eDllInfo = R_getEmbeddingDllInfo();
+    // TODO:
+	// printf("Calling R_getEmbeddingDllInfo...\n");
+	// DllInfo *eDllInfo = R_getEmbeddingDllInfo();
+	printf("Running R with Rf_mainloop...\n");
 	Rf_mainloop();
+	printf("Closing R with Rf_endEmbeddedR...\n");
 	Rf_endEmbeddedR(0);
+	printf("Done");
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
index 70ac71adbb..8dd00386cd 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
@@ -45,8 +45,8 @@ import org.junit.runner.Result;
 
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RSuicide;
 import com.oracle.truffle.r.runtime.ResourceHandlerFactory;
-import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
 import com.oracle.truffle.r.test.generate.FastRSession;
@@ -230,7 +230,7 @@ public class TestBase {
                     if (updated) {
                         if (expectedOutputManager.checkOnly) {
                             // fail fast
-                            Utils.rSuicideDefault("Test file:" + expectedOutputManager.outputFile + " is out of sync with unit tests");
+                            RSuicide.rSuicideDefault("Test file:" + expectedOutputManager.outputFile + " is out of sync with unit tests");
                         }
                         System.out.println("updating " + expectedOutputManager.outputFile);
                     }
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index e129a62211..f3632cb367 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -58,7 +58,7 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.ja
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Download.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java,gnu_r.copyright
-com.oracle.truffle.r.native/fficall/src/truffle_common/Rembedded.c,gnu_r.copyright
+com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rembedded.c,gnu_r.copyright
 com.oracle.truffle.r.native/fficall/src/common/arithmetic_fastr.c,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.native/fficall/src/common/coerce_fastr.c,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.native/fficall/src/common/errors_fastr.c,gnu_r.core.copyright
-- 
GitLab