From 74734c16dbe2481ca45bb975e612875f1513ef82 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Fri, 29 May 2015 10:47:12 +0200
Subject: [PATCH] restructure foreign function builtins into separate classes

---
 .../r/library/graphics/GraphicsCCalls.java    |   29 +-
 .../library/methods/MethodsListDispatch.java  |   96 +-
 .../library/methods/SetPrimitiveMethods.java  |   33 -
 .../truffle/r/library/stats/Covcor.java       |   42 +-
 .../r/library/stats/GammaFunctions.java       |   22 +-
 .../truffle/r/library/stats/Random2.java      |   31 -
 .../oracle/truffle/r/library/stats/Rnorm.java |   52 +
 .../oracle/truffle/r/library/stats/Runif.java |   22 +-
 .../r/library/stats/SplineFunctions.java      |   35 +-
 .../{ToolsParseRd.java => C_ParseRd.java}     |   15 +-
 .../{ToolsDirChmod.java => DirChmod.java}     |   26 +-
 .../tools/{ToolsRmd5.java => Rmd5.java}       |    9 +-
 .../truffle/r/library/tools/ToolsText.java    |  130 +-
 .../truffle/r/library/utils/CountFields.java  |   67 +-
 .../oracle/truffle/r/library/utils/Crc64.java |   12 +-
 .../truffle/r/library/utils/Download.java     |   30 +-
 .../oracle/truffle/r/library/utils/Menu.java  |   24 +-
 .../truffle/r/library/utils/TypeConvert.java  |    7 +-
 .../truffle/r/library/utils/WriteTable.java   |   94 +-
 .../r/nodes/builtin/base/BasePackage.java     |    3 +-
 .../nodes/builtin/base/ForeignFunctions.java  | 1095 -----------------
 .../builtin/base/foreign/CairoProps.java      |   25 +
 .../r/nodes/builtin/base/foreign/DotC.java    |  166 +++
 .../r/nodes/builtin/base/foreign/Dqrcf.java   |   64 +
 .../r/nodes/builtin/base/foreign/Dqrdc2.java  |   59 +
 .../r/nodes/builtin/base/foreign/Fft.java     |   89 ++
 .../builtin/base/foreign/Flushconsole.java    |   23 +
 .../base/foreign/ForeignFunctions.java        |  309 +++++
 .../base/foreign/MakeQuartzDefault.java       |   24 +
 .../builtin/base/foreign/ReadTableHead.java   |   36 +
 .../r/nodes/builtin/RExternalBuiltinNode.java |  181 +++
 .../oracle/truffle/r/runtime/RContext.java    |   14 +
 mx.fastr/copyrights/overrides                 |   13 +-
 mx.fastr/suite.py                             |    4 +
 34 files changed, 1559 insertions(+), 1322 deletions(-)
 delete mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SetPrimitiveMethods.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java
 rename com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/{ToolsParseRd.java => C_ParseRd.java} (66%)
 rename com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/{ToolsDirChmod.java => DirChmod.java} (63%)
 rename com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/{ToolsRmd5.java => Rmd5.java} (89%)
 delete mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CairoProps.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Flushconsole.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/MakeQuartzDefault.java
 create mode 100644 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java
index f8be136a75..cf5fd17e35 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java
@@ -14,20 +14,34 @@
  */
 package com.oracle.truffle.r.library.graphics;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.library.graphics.core.*;
 import com.oracle.truffle.r.library.graphics.core.geometry.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 public class GraphicsCCalls {
-    public static void plotXy(RDoubleVector xyVector) {
-        assert xyVector.getLength() % 2 == 0 : "wrong size of vector";
-        getGraphicsEngine().setCurrentGraphicsDeviceMode(GraphicsDevice.Mode.GRAPHICS_ON);
-        drawWithLines(xyVector);
+    public static final class C_PlotXY extends RExternalBuiltinNode {
+
+        @Override
+        @TruffleBoundary
+        public RNull call(RArgsValuesAndNames args) {
+            RDoubleVector xyVector = ((RAbstractDoubleVector) args.getArgument(0)).materialize();
+            assert xyVector.getLength() % 2 == 0 : "wrong size of vector";
+            getGraphicsEngine().setCurrentGraphicsDeviceMode(GraphicsDevice.Mode.GRAPHICS_ON);
+            drawWithLines(xyVector);
+            return RNull.instance;
+        }
     }
 
-    public static Object par(@SuppressWarnings("unused") RArgsValuesAndNames args) {
-        // pch
-        return RDataFactory.createIntVectorFromScalar(1);
+    public static final class C_Par extends RExternalBuiltinNode {
+
+        @Override
+        public Object call(RArgsValuesAndNames args) {
+            // pch
+            return RDataFactory.createIntVectorFromScalar(1);
+        }
     }
 
     private static void drawWithLines(RDoubleVector xyVector) {
@@ -55,5 +69,4 @@ public class GraphicsCCalls {
     private static GraphicsEngine getGraphicsEngine() {
         return GraphicsEngineImpl.getInstance();
     }
-
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
index 6315dfffec..8a3d73eefe 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
@@ -11,57 +11,93 @@
  */
 package com.oracle.truffle.r.library.methods;
 
+import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
 
 // Transcribed from src/library/methods/methods_list_dispatch.c
 
 public class MethodsListDispatch {
-    private static MethodsListDispatch singleton = new MethodsListDispatch();
 
-    private boolean tableDispatchOn = true;
+    public abstract static class R_initMethodDispatch extends RExternalBuiltinNode.Arg1 {
 
-    public static MethodsListDispatch getInstance() {
-        return singleton;
+        @Specialization
+        @TruffleBoundary
+        protected REnvironment initMethodDispatch(REnvironment env) {
+            // TBD what should we actually do here
+            return env;
+        }
     }
 
-    public REnvironment initMethodDispatch(REnvironment env) {
-        // TODO initialize
-        return env;
+    public abstract static class R_methodsPackageMetaName extends RExternalBuiltinNode.Arg3 {
+
+        @TruffleBoundary
+        @Specialization
+        protected String callMethodsPackageMetaName(String prefixString, String nameString, String pkgString) {
+            if (pkgString.length() == 0) {
+                return ".__" + prefixString + "__" + nameString;
+            } else {
+                return ".__" + prefixString + "__" + nameString + ":" + pkgString;
+            }
+        }
     }
 
-    public byte setMethodDispatch(byte onOff) {
-        boolean prev = tableDispatchOn;
+    public abstract static class R_getClassFromCache extends RExternalBuiltinNode.Arg2 {
 
-        if (onOff == RRuntime.LOGICAL_NA) {
-            return RRuntime.asLogical(prev);
-        }
-        boolean value = RRuntime.fromLogical(onOff);
-        tableDispatchOn = value;
-        if (value != prev) {
-            // TODO
+        @TruffleBoundary
+        @Specialization
+        protected Object callGetClassFromCache(REnvironment table, Object klass) {
+            String klassString = RRuntime.asString(klass);
+
+            if (klassString != null) {
+                Object value = table.get(klassString);
+                if (value == null) {
+                    return RNull.instance;
+                } else {
+                    // TODO check PACKAGE equality
+                    return value;
+                }
+            } else {
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARG_TYPE);
+            }
         }
-        return RRuntime.asLogical(prev);
     }
 
-    public String methodsPackageMetaName(String prefixString, String nameString, String pkgString) {
-        if (pkgString.length() == 0) {
-            return String.format(".__%s__%s", prefixString, nameString);
-        } else {
-            return String.format(".__%s__%s:%s", prefixString, nameString, pkgString);
+    public abstract static class R_set_method_dispatch extends RExternalBuiltinNode.Arg1 {
+
+        @TruffleBoundary
+        @Specialization
+        protected Object callSetMethodDispatch(RAbstractLogicalVector onOffVector) {
+            boolean prev = RContext.getInstance().isMethodTableDispatchOn();
+            byte onOff = castLogical(onOffVector);
+
+            if (onOff == RRuntime.LOGICAL_NA) {
+                return RRuntime.asLogical(prev);
+            }
+            boolean value = RRuntime.fromLogical(onOff);
+            RContext.getInstance().setMethodTableDispatchOn(value);
+            if (value != prev) {
+                // TODO
+            }
+            return RRuntime.asLogical(prev);
         }
     }
 
-    public Object getClassFromCache(REnvironment table, String klassString) {
-        Object value = table.get(klassString);
-        if (value == null) {
+    public abstract static class R_M_setPrimitiveMethods extends RExternalBuiltinNode.Arg5 {
+
+        @SuppressWarnings("unused")
+        @Specialization
+        @TruffleBoundary
+        protected Object setPrimitiveMethods(Object fname, Object op, Object codeVec, RFunction fundef, Object mlist) {
+            String fnameString = RRuntime.asString(fname);
+            String codeVecString = RRuntime.asString(codeVec);
+
+            // TODO implement
             return RNull.instance;
-        } else {
-            // TODO check PACKAGE equality
-            return value;
         }
-
     }
-
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SetPrimitiveMethods.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SetPrimitiveMethods.java
deleted file mode 100644
index 54d302e715..0000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SetPrimitiveMethods.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, 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.library.methods;
-
-import com.oracle.truffle.r.runtime.data.*;
-
-public class SetPrimitiveMethods {
-    @SuppressWarnings("unused")
-    public static Object doit(String fname, Object op, String codeVec, RFunction fundef, Object mlist) {
-        // TODO implement
-        return RNull.instance;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
index d22ff41dac..8c111c183e 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
@@ -13,15 +13,39 @@ package com.oracle.truffle.r.library.stats;
 
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.*;
 
 /*
  * Logic derived from GNU-R, library/stats/src/cov.c
  */
-public class Covcor {
-    private static final Covcor singleton = new Covcor();
+public final class Covcor extends RExternalBuiltinNode {
+
+    private final boolean isCor;
+
+    public Covcor(boolean isCor) {
+        this.isCor = isCor;
+    }
+
+    @Override
+    public Object call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        if (argValues[0] == RNull.instance) {
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.IS_NULL, "x");
+        }
+        // TODO error checks/coercions
+        RAbstractDoubleVector x = (RAbstractDoubleVector) argValues[0];
+        RAbstractDoubleVector y = argValues[1] == RNull.instance ? null : (RAbstractDoubleVector) argValues[1];
+        int method = ((RAbstractIntVector) argValues[2]).getDataAt(0);
+        if (method != 4) {
+            throw RError.nyi(getEncapsulatingSourceSection(), "method");
+        }
+        boolean iskendall = RRuntime.fromLogical(castLogical(castVector(argValues[3])));
+        return corcov(x.materialize(), y != null ? y.materialize() : null, method, iskendall, getEncapsulatingSourceSection());
+    }
 
     private final NACheck check = new NACheck();
 
@@ -35,11 +59,7 @@ public class Covcor {
     private final BranchProfile error = BranchProfile.create();
     private final BranchProfile warning = BranchProfile.create();
 
-    public static Covcor getInstance() {
-        return singleton;
-    }
-
-    public RDoubleVector corcov(RDoubleVector x, RDoubleVector y, @SuppressWarnings("unused") int method, boolean iskendall, boolean cor, SourceSection src) throws RError {
+    public RDoubleVector corcov(RDoubleVector x, RDoubleVector y, @SuppressWarnings("unused") int method, boolean iskendall, SourceSection src) throws RError {
         boolean ansmat;
         boolean naFail;
         boolean everything;
@@ -96,20 +116,20 @@ public class Covcor {
         double[] xm = new double[ncx];
         if (y == null) {
             if (everything) {
-                sd0 = covNA1(n, ncx, x, xm, answerData, cor, iskendall);
+                sd0 = covNA1(n, ncx, x, xm, answerData, isCor, iskendall);
             } else {
                 RIntVector ind = RDataFactory.createIntVector(n);
                 complete1(n, ncx, x, ind, naFail);
-                sd0 = covComplete1(n, ncx, x, xm, ind, answerData, cor, iskendall);
+                sd0 = covComplete1(n, ncx, x, xm, ind, answerData, isCor, iskendall);
             }
         } else {
             double[] ym = new double[ncy];
             if (everything) {
-                sd0 = covNA2(n, ncx, ncy, x, y, xm, ym, answerData, cor, iskendall);
+                sd0 = covNA2(n, ncx, ncy, x, y, xm, ym, answerData, isCor, iskendall);
             } else {
                 RIntVector ind = RDataFactory.createIntVector(n);
                 complete2(n, ncx, ncy, x, y, ind, naFail);
-                sd0 = covComplete2(n, ncx, ncy, x, y, xm, ym, ind, answerData, cor, iskendall);
+                sd0 = covComplete2(n, ncx, ncy, x, y, xm, ym, ind, answerData, isCor, iskendall);
             }
         }
 
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/GammaFunctions.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/GammaFunctions.java
index 1ecdb1545f..1f912aa3e1 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/GammaFunctions.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/GammaFunctions.java
@@ -20,7 +20,10 @@ package com.oracle.truffle.r.library.stats;
 
 import static com.oracle.truffle.r.library.stats.StatsUtil.*;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
@@ -36,18 +39,12 @@ public abstract class GammaFunctions {
 
     // This is derived from distn.c.
 
-    public static class Qgamma {
-
-        private static final Qgamma singleton = new Qgamma();
+    public abstract static class Qgamma extends RExternalBuiltinNode.Arg5 {
 
         private final NACheck naCheck = NACheck.create();
 
-        public static Qgamma getInstance() {
-            return singleton;
-        }
-
         @TruffleBoundary
-        public RDoubleVector qgamma(RAbstractDoubleVector p, RAbstractDoubleVector shape, RAbstractDoubleVector scale, byte lowerTail, byte logP, RAttributeProfiles attrProfiles) {
+        private RDoubleVector qgamma(RAbstractDoubleVector p, RAbstractDoubleVector shape, RAbstractDoubleVector scale, byte lowerTail, byte logP) {
             int pLen = p.getLength();
             int shapeLen = shape.getLength();
             int scaleLen = scale.getLength();
@@ -69,6 +66,13 @@ public abstract class GammaFunctions {
             return res;
         }
 
+        @Specialization
+        public RAbstractDoubleVector qgamma(RAbstractDoubleVector p, RAbstractDoubleVector shape, RAbstractDoubleVector scale, RAbstractLogicalVector lowerTail, RAbstractLogicalVector logP) {
+            if (shape.getLength() == 0 || scale.getLength() == 0) {
+                return RDataFactory.createEmptyDoubleVector();
+            }
+            return qgamma(p, shape, scale, castLogical(lowerTail), castLogical(logP));
+        }
     }
 
     // The remainder of this file is derived from GNU R (mostly nmath): qgamma.c, nmath.h, lgamma.c,
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Random2.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Random2.java
index 1836629ff5..98b96b9af2 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Random2.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Random2.java
@@ -13,41 +13,11 @@ package com.oracle.truffle.r.library.stats;
 
 import static com.oracle.truffle.r.library.stats.StatsUtil.*;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.rng.*;
-
 /*
  * Logic derived from GNU-R, see inline comments.
  */
 public class Random2 {
 
-    @TruffleBoundary
-    public static RDoubleVector rnorm(int n, double mean, double standardd) {
-        double[] result = new double[n];
-        for (int i = 0; i < n; i++) {
-            result[i] = generateNorm(mean, standardd);
-        }
-        return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
-    }
-
-    // from GNUR: rnorm.c
-    private static double generateNorm(double mean, double standardd) {
-        return mean + standardd * normRand();
-    }
-
-    // from GNUR: snorm.c
-    private static double normRand() {
-        double u1;
-
-        // case INVERSION:
-        double big = 134217728; /* 2^27 */
-        /* unif_rand() alone is not of high enough precision */
-        u1 = RRNG.unifRand();
-        u1 = (int) (big * u1) + RRNG.unifRand();
-        return qnorm5(u1 / big, 0.0, 1.0, true, false);
-    }
-
     // from GNUR: qnorm.c
     public static double qnorm5(double p, double mu, double sigma, boolean lowerTail, boolean logP) {
         double pU;
@@ -110,5 +80,4 @@ public class Random2 {
         }
         return mu + sigma * val;
     }
-
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java
new file mode 100644
index 0000000000..d620d9ea8e
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java
@@ -0,0 +1,52 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.rng.*;
+
+/**
+ * TODO GnuR checks/updates {@code .Random.seed} across this call. TODO Honor min/max.
+ */
+public abstract class Rnorm extends RExternalBuiltinNode.Arg3 {
+
+    // from GNUR: rnorm.c
+    private static double generateNorm(double mean, double standardd) {
+        return mean + standardd * normRand();
+    }
+
+    // from GNUR: snorm.c
+    private static double normRand() {
+        double u1;
+
+        // case INVERSION:
+        double big = 134217728; /* 2^27 */
+        /* unif_rand() alone is not of high enough precision */
+        u1 = RRNG.unifRand();
+        u1 = (int) (big * u1) + RRNG.unifRand();
+        return Random2.qnorm5(u1 / big, 0.0, 1.0, true, false);
+    }
+
+    @Specialization
+    protected Object doRnorm(Object n, double mean, double standardd) {
+        // TODO full error checks
+        int nInt = castInt(castVector(n));
+
+        double[] result = new double[nInt];
+        for (int i = 0; i < nInt; i++) {
+            result[i] = generateNorm(mean, standardd);
+        }
+        return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java
index f99c9b47c3..87e0d5d3f9 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Runif.java
@@ -22,20 +22,28 @@
  */
 package com.oracle.truffle.r.library.stats;
 
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.rng.*;
 
 /**
- * TODO GnuR checks/updates {@code .Random.seed} across this call. TODO Honor min/max.
+ * TODO GnuR checks/updates {@code .Random.seed} across this call.
  */
-public class Runif {
+public abstract class Runif extends RExternalBuiltinNode.Arg3 {
 
-    public static RDoubleVector runif(int length, @SuppressWarnings("unused") double min, @SuppressWarnings("unused") double max) {
-        double[] result = new double[length];
-        for (int i = 0; i < length; i++) {
-            result[i] = RRNG.unifRand();
+    @Specialization
+    protected Object doRunif(Object n, Object min, Object max) {
+        // TODO full error checks
+        int nInt = castInt(castVector(n));
+        double minDouble = castDouble(castVector(min)).getDataAt(0);
+        double maxDouble = castDouble(castVector(max)).getDataAt(0);
+        double delta = maxDouble - minDouble;
+
+        double[] result = new double[nInt];
+        for (int i = 0; i < nInt; i++) {
+            result[i] = minDouble + RRNG.unifRand() * delta;
         }
         return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
     }
-
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java
index 9ab592fbc0..d65e0aaa92 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java
@@ -11,9 +11,12 @@
  */
 package com.oracle.truffle.r.library.stats;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.*;
 
 /**
@@ -24,8 +27,27 @@ import com.oracle.truffle.r.runtime.ops.*;
  */
 public class SplineFunctions {
 
-    @TruffleBoundary
-    public static RList splineCoef(int method, RDoubleVector x, RDoubleVector y) {
+    public abstract static class SplineCoef extends RExternalBuiltinNode.Arg3 {
+
+        @TruffleBoundary
+        @Specialization
+        protected Object splineCoef(Object method, RAbstractDoubleVector x, RAbstractDoubleVector y) {
+            int methodInt = castInt(castVector(method));
+            return SplineFunctions.splineCoef(methodInt, x.materialize(), y.materialize());
+        }
+    }
+
+    public abstract static class SplineEval extends RExternalBuiltinNode.Arg2 {
+
+        @TruffleBoundary
+        @Specialization
+        protected Object splineEval(RAbstractDoubleVector xout, RList z) {
+            // This is called with the result of SplineCoef, so it is surely an RList
+            return SplineFunctions.splineEval(attrProfiles, xout.materialize(), z);
+        }
+    }
+
+    private static RList splineCoef(int method, RDoubleVector x, RDoubleVector y) {
         final int n = x.getLength();
         if (y.getLength() != n) {
             throw RError.error(RError.Message.INPUTS_DIFFERENT_LENGTHS);
@@ -63,7 +85,7 @@ public class SplineFunctions {
     /*
      * Periodic Spline --------------- The end conditions here match spline (and its derivatives) at
      * x[1] and x[n].
-     *
+     * 
      * Note: There is an explicit check that the user has supplied data with y[1] equal to y[n].
      */
     private static void periodicSpline(int n, double[] x, double[] y, double[] b, double[] c, double[] d) {
@@ -182,7 +204,7 @@ public class SplineFunctions {
     /*
      * Natural Splines --------------- Here the end-conditions are determined by setting the second
      * derivative of the spline at the end-points to equal to zero.
-     *
+     * 
      * There are n-2 unknowns (y[i]'' at x[2], ..., x[n-1]) and n-2 equations to determine them.
      * Either Choleski or Gaussian elimination could be used.
      */
@@ -340,7 +362,7 @@ public class SplineFunctions {
         return;
     }
 
-    public static RDoubleVector splineEval(RAttributeProfiles attrProfiles, RDoubleVector xout, RList z) {
+    private static RDoubleVector splineEval(RAttributeProfiles attrProfiles, RDoubleVector xout, RList z) {
         int nu = xout.getLength();
         double[] yout = new double[nu];
         int method = (int) z.getDataAt(z.getElementIndexByName(attrProfiles, "method"));
@@ -356,7 +378,6 @@ public class SplineFunctions {
         return RDataFactory.createDoubleVector(yout, xout.isComplete() && x.isComplete() && y.isComplete());
     }
 
-    @TruffleBoundary
     private static void splineEval(int method, int nu, double[] u, double[] v, int n, double[] x, double[] y, double[] b, double[] c, double[] d) {
         /*
          * Evaluate v[l] := spline(u[l], ...), l = 1,..,nu, i.e. 0:(nu-1) Nodes x[i], coef (y[i];
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsParseRd.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
similarity index 66%
rename from com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsParseRd.java
rename to com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
index 41e550fda8..2313f8e80d 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsParseRd.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
@@ -22,14 +22,25 @@
  */
 package com.oracle.truffle.r.library.tools;
 
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
 
-public class ToolsParseRd {
+public abstract class C_ParseRd extends RExternalBuiltinNode.Arg7 {
+
     @SuppressWarnings("unused")
-    public static Object parseRd(RConnection con, REnvironment srcfile, String encoding, boolean verbose, RAbstractStringVector basename, boolean fragment, boolean warningCalls) {
+    @Specialization
+    protected Object parseRd(RConnection con, REnvironment srcfile, String encoding, byte verbose, RAbstractStringVector basename, byte fragment, byte warningCalls) {
         return RNull.instance;
     }
+
+    @SuppressWarnings("unused")
+    @Fallback
+    public Object parseRd(Object con, Object srcfile, Object encoding, Object verbose, Object basename, Object fragment, Object warningCalls) {
+        throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsDirChmod.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
similarity index 63%
rename from com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsDirChmod.java
rename to com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
index 989a524747..9691ccb32c 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsDirChmod.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
@@ -17,16 +17,30 @@ import java.nio.file.attribute.*;
 import java.util.*;
 import java.util.stream.*;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ffi.*;
 
-public class ToolsDirChmod {
+public abstract class DirChmod extends RExternalBuiltinNode.Arg2 {
+
     private static final int GRPWRITE_FILE_MASK = 0664;
     private static final int GRPWRITE_DIR_MASK = 0775;
     private static final int FILE_MASK = 0644;
     private static final int DIR_MASK = 0755;
 
-    public static void dirChmod(String pathName, boolean setGroupWrite) {
+    @TruffleBoundary
+    @Specialization
+    protected RNull dirChmod(RAbstractStringVector dir, Object gws) {
+        if (dir.getLength() != 1) {
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "dir");
+        }
+        String pathName = ((RStringVector) dir).getDataAt(0);
+        boolean setGroupWrite = RRuntime.fromLogical(castLogical(castVector(gws)));
+
         Path path = FileSystems.getDefault().getPath(pathName);
         int fileMask = setGroupWrite ? GRPWRITE_FILE_MASK : FILE_MASK;
         int dirMask = setGroupWrite ? GRPWRITE_DIR_MASK : DIR_MASK;
@@ -41,12 +55,18 @@ public class ToolsDirChmod {
                 PosixFileAttributes pfa = Files.readAttributes(element, PosixFileAttributes.class);
                 int elementMode = Utils.intFilePermissions(pfa.permissions());
                 int newMode = Files.isDirectory(element) ? elementMode | dirMask : elementMode | fileMask;
-// System.out.printf("path %s: old %o, new %o%n", element, elementMode, newMode);
+                // System.out.printf("path %s: old %o, new %o%n", element, elementMode, newMode);
                 RFFIFactory.getRFFI().getBaseRFFI().chmod(element.toString(), newMode);
             }
         } catch (IOException ex) {
             // ignore
         }
+        return RNull.instance;
+    }
 
+    @SuppressWarnings("unused")
+    @Fallback
+    protected Object fallback(Object dir, Object gws) {
+        throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "dir");
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsRmd5.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/Rmd5.java
similarity index 89%
rename from com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsRmd5.java
rename to com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/Rmd5.java
index 4ab5cb6b41..4767f7201a 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsRmd5.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/Rmd5.java
@@ -26,12 +26,17 @@ import java.io.*;
 import java.security.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
-public class ToolsRmd5 {
+public abstract class Rmd5 extends RExternalBuiltinNode.Arg1 {
+
+    @Specialization
     @TruffleBoundary
-    public static RStringVector rmd5(RStringVector files) {
+    protected RStringVector rmd5(RAbstractStringVector files) {
         MessageDigest digest;
         boolean complete = RDataFactory.COMPLETE_VECTOR;
         try {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java
index 7927973e42..b30d8b603d 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java
@@ -13,76 +13,96 @@ package com.oracle.truffle.r.library.tools;
 
 import java.io.*;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 public class ToolsText {
-    public static RStringVector doTabExpand(RStringVector strings, RIntVector starts) {
-        String[] data = new String[strings.getLength()];
-        for (int i = 0; i < data.length; i++) {
-            String input = strings.getDataAt(i);
-            if (input.indexOf('\t') >= 0) {
-                StringBuffer sb = new StringBuffer();
-                int b = 0;
-                int start = starts.getDataAt(i % data.length);
-                for (int sx = 0; sx < input.length(); sx++) {
-                    char ch = input.charAt(sx);
-                    if (ch == '\n') {
-                        start = -b - 1;
-                    }
-                    if (ch == '\t') {
-                        do {
-                            sb.append(' ');
+
+    public abstract static class DoTabExpand extends RExternalBuiltinNode.Arg2 {
+
+        @TruffleBoundary
+        @Specialization
+        protected Object doTabExpand(RAbstractStringVector strings, RAbstractIntVector starts) {
+            String[] data = new String[strings.getLength()];
+            for (int i = 0; i < data.length; i++) {
+                String input = strings.getDataAt(i);
+                if (input.indexOf('\t') >= 0) {
+                    StringBuffer sb = new StringBuffer();
+                    int b = 0;
+                    int start = starts.getDataAt(i % data.length);
+                    for (int sx = 0; sx < input.length(); sx++) {
+                        char ch = input.charAt(sx);
+                        if (ch == '\n') {
+                            start = -b - 1;
+                        }
+                        if (ch == '\t') {
+                            do {
+                                sb.append(' ');
+                                b++;
+                            } while (((b + start) & 7) != 0);
+                        } else {
+                            sb.append(ch);
                             b++;
-                        } while (((b + start) & 7) != 0);
-                    } else {
-                        sb.append(ch);
-                        b++;
+                        }
                     }
+                    data[i] = sb.toString();
+                } else {
+                    data[i] = input;
                 }
-                data[i] = sb.toString();
-            } else {
-                data[i] = input;
             }
+            return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR);
         }
-        return RDataFactory.createStringVector(data, RDataFactory.COMPLETE_VECTOR);
     }
 
-    @TruffleBoundary
-    public static RLogicalVector filesAppendLF(String file1, RStringVector file2Vec) {
-        int n2 = file2Vec.getLength();
-        byte[] data = new byte[n2];
-        if (!RRuntime.isNA(file1)) {
-            try (BufferedWriter out = new BufferedWriter(new FileWriter(file1, true))) {
-                for (int i = 0; i < file2Vec.getLength(); i++) {
-                    String path2 = file2Vec.getDataAt(i);
-                    if (RRuntime.isNA(path2)) {
-                        continue;
-                    }
-                    File path2File = new File(path2);
-                    if (!(path2File.exists() && path2File.canRead())) {
-                        continue;
-                    }
-                    char[] path2Data = new char[(int) path2File.length()];
-                    try (BufferedReader in = new BufferedReader(new FileReader(path2File))) {
-                        out.write("#line 1 \"" + path2 + "\"\n");
-                        in.read(path2Data);
-                        out.write(path2Data);
-                        if (!(path2Data.length > 0 && path2Data[path2Data.length - 1] == '\n')) {
-                            out.write('\n');
+    public abstract static class CodeFilesAppend extends RExternalBuiltinNode.Arg2 {
+
+        @TruffleBoundary
+        @Specialization
+        protected Object codeFilesAppend(RAbstractStringVector file1Vector, RAbstractStringVector file2) {
+            if (file1Vector.getLength() != 1) {
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "file1");
+            }
+            if (file2.getLength() < 1) {
+                return RDataFactory.createEmptyLogicalVector();
+            }
+            String file1 = file1Vector.getDataAt(0);
+            int n2 = file2.getLength();
+            byte[] data = new byte[n2];
+            if (!RRuntime.isNA(file1)) {
+                try (BufferedWriter out = new BufferedWriter(new FileWriter(file1, true))) {
+                    for (int i = 0; i < file2.getLength(); i++) {
+                        String path2 = file2.getDataAt(i);
+                        if (RRuntime.isNA(path2)) {
+                            continue;
+                        }
+                        File path2File = new File(path2);
+                        if (!(path2File.exists() && path2File.canRead())) {
+                            continue;
+                        }
+                        char[] path2Data = new char[(int) path2File.length()];
+                        try (BufferedReader in = new BufferedReader(new FileReader(path2File))) {
+                            out.write("#line 1 \"" + path2 + "\"\n");
+                            in.read(path2Data);
+                            out.write(path2Data);
+                            if (!(path2Data.length > 0 && path2Data[path2Data.length - 1] == '\n')) {
+                                out.write('\n');
+                            }
+                            data[i] = RRuntime.LOGICAL_TRUE;
+                        } catch (IOException ex) {
+                            RError.warning(RError.Message.GENERIC, "write error during file append");
+                            // shouldn't happen, just continue with false result
                         }
-                        data[i] = RRuntime.LOGICAL_TRUE;
-                    } catch (IOException ex) {
-                        RError.warning(RError.Message.GENERIC, "write error during file append");
-                        // shouldn't happen, just continue with false result
-                    }
 
+                    }
+                } catch (IOException ex) {
+                    // just return logical false
                 }
-            } catch (IOException ex) {
-                // just return logical false
             }
+            return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR);
         }
-        return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR);
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java
index e9f0b0967f..b14e6ce0da 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java
@@ -14,6 +14,7 @@ package com.oracle.truffle.r.library.utils;
 import java.io.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.data.*;
@@ -21,11 +22,12 @@ import com.oracle.truffle.r.runtime.data.*;
 // Transcribed from GnuR, library/utils/src/io.c
 
 // Checkstyle: stop
-@SuppressWarnings("unused")
-public class CountFields {
+public final class CountFields extends RExternalBuiltinNode {
+
     private static final int R_EOF = -1;
     private static final int SCAN_BLOCKSIZE = 1000;
 
+    @SuppressWarnings("unused")
     private static class LocalData {
         Object NAstrings;
         boolean quiet;
@@ -47,7 +49,7 @@ public class CountFields {
     }
 
     @TruffleBoundary
-    public static Object execute(RConnection file, char sepChar, String quoteSet, int nskip, boolean blskip, char comChar) throws IOException {
+    private static Object countFields(RConnection file, char sepChar, String quoteSet, @SuppressWarnings("unused") int nskip, boolean blskip, char comChar) throws IOException {
         LocalData data = new LocalData();
         data.sepchar = sepChar;
         data.comchar = comChar;
@@ -181,7 +183,7 @@ public class CountFields {
         return c;
     }
 
-    private static void unscanchar(int c, LocalData d) throws IOException {
+    private static void unscanchar(int c, LocalData d) {
         d.save = c;
     }
 
@@ -282,4 +284,61 @@ public class CountFields {
         return false;
     }
 
+    // Transcribed from GnuR, library/utils/src/io.c
+    @Override
+    public Object call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        RConnection conn = (RConnection) argValues[0];
+        Object sepArg = argValues[1];
+        char sepChar;
+        Object quoteArg = argValues[2];
+        int nskip = castInt(castVector(argValues[3]));
+        byte blskip = castLogical(castVector(argValues[4]));
+        String commentCharArg = isString(argValues[5]);
+        char comChar;
+        if (!(commentCharArg != null && commentCharArg.length() == 1)) {
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "comment.char");
+        } else {
+            comChar = commentCharArg.charAt(0);
+        }
+
+        if (nskip < 0 || nskip == RRuntime.INT_NA) {
+            nskip = 0;
+        }
+        if (blskip == RRuntime.LOGICAL_NA) {
+            blskip = RRuntime.LOGICAL_TRUE;
+        }
+
+        if (sepArg instanceof RNull) {
+            sepChar = 0;
+        } else {
+            String s = isString(sepArg);
+            if (s == null) {
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "sep");
+            } else {
+                if (s.length() == 0) {
+                    sepChar = 0;
+                } else {
+                    sepChar = s.charAt(0);
+                }
+            }
+        }
+        String quoteSet;
+        if (quoteArg instanceof RNull) {
+            quoteSet = "";
+        } else {
+            String s = isString(quoteArg);
+            if (s == null) {
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "invalid quote symbol set");
+            } else {
+                quoteSet = s;
+            }
+        }
+        try (RConnection openConn = conn.forceOpen("r")) {
+            return countFields(openConn, sepChar, quoteSet, nskip, RRuntime.fromLogical(blskip), comChar);
+        } catch (IllegalStateException | IOException ex) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage());
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Crc64.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Crc64.java
index cb75d43aba..359da6fe97 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Crc64.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Crc64.java
@@ -25,15 +25,15 @@ package com.oracle.truffle.r.library.utils;
 import java.security.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 
-public class Crc64 {
-    /**
-     * GnuR uses lzma, but unless we care about exact match (and the usage doesn't suggest we need
-     * to, we can use any algorithm that provides a unique-ish result.
-     */
+public abstract class Crc64 extends RExternalBuiltinNode.Arg1 {
+
     @TruffleBoundary
-    public static String crc64(String input) {
+    @Specialization
+    protected String crc64(String input) {
         try {
             MessageDigest md = MessageDigest.getInstance("MD5");
             byte[] digest = md.digest(input.getBytes());
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Download.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Download.java
index affe5ed6e5..d49c612cc3 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Download.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Download.java
@@ -25,13 +25,18 @@ package com.oracle.truffle.r.library.utils;
 import java.io.*;
 import java.net.*;
 
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+
 /**
  * Support for the "internal"method of "utils::download.file". TODO take note of "quiet", "mode" and
  * "cacheOK".
  */
-public class Download {
+public final class Download extends RExternalBuiltinNode {
+
     @SuppressWarnings("unused")
-    public static void download(String urlString, String destFile, boolean quiet, String mode, boolean cacheOK) throws IOException {
+    private static void download(String urlString, String destFile, boolean quiet, String mode, boolean cacheOK) throws IOException {
         URL url = new URL(urlString);
         byte[] buffer = new byte[8192];
         try (BufferedInputStream in = new BufferedInputStream(url.openStream()); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destFile))) {
@@ -41,4 +46,25 @@ public class Download {
             }
         }
     }
+
+    @Override
+    public Integer call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        String url = isString(argValues[0]);
+        String destFile = isString(argValues[1]);
+        byte quiet = castLogical(castVector(argValues[2]));
+        String mode = isString(argValues[3]);
+        byte cacheOK = castLogical(castVector(argValues[4]));
+        if (url == null || destFile == null || mode == null) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_UNNAMED_ARGUMENTS);
+        }
+        try {
+            Download.download(url, destFile, RRuntime.fromLogical(quiet), mode, RRuntime.fromLogical(cacheOK));
+            return 0;
+        } catch (IOException ex) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage());
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java
index 1b07f13b51..dbf820d8c8 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java
@@ -11,15 +11,21 @@
  */
 package com.oracle.truffle.r.library.utils;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RContext.*;
+import com.oracle.truffle.r.runtime.RContext.ConsoleHandler;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 // Translated from GnuR: library/utils/io.c
 
-public class Menu {
-    public static int menu(String[] choices) {
+public abstract class Menu extends RExternalBuiltinNode.Arg1 {
+
+    @Specialization
+    protected int menu(RAbstractStringVector choices) {
         ConsoleHandler ch = RContext.getInstance().getConsoleHandler();
-        int first = choices.length + 1;
+        int first = choices.getLength() + 1;
         ch.print("Selection: ");
         String response = ch.readLine().trim();
         if (response.length() > 0) {
@@ -30,8 +36,8 @@ public class Menu {
                     //
                 }
             } else {
-                for (int i = 0; i < choices.length; i++) {
-                    String entry = choices[i];
+                for (int i = 0; i < choices.getLength(); i++) {
+                    String entry = choices.getDataAt(i);
                     if (entry.equals(response)) {
                         first = i + 1;
                         break;
@@ -41,4 +47,10 @@ public class Menu {
         }
         return first;
     }
+
+    @Fallback
+    @TruffleBoundary
+    protected int menu(@SuppressWarnings("unused") Object choices) {
+        throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "choices");
+    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java
index 27b13c2c98..2b91e0137c 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java
@@ -24,11 +24,13 @@ package com.oracle.truffle.r.library.utils;
 
 import java.util.*;
 
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
-public class TypeConvert {
+public abstract class TypeConvert extends RExternalBuiltinNode.Arg5 {
 
     private static boolean isNA(String s, RAbstractStringVector naStrings) {
         for (int i = 0; i < naStrings.getLength(); i++) {
@@ -72,7 +74,8 @@ public class TypeConvert {
         return RDataFactory.createLogicalVector(data, firstPos > 0 ? RDataFactory.INCOMPLETE_VECTOR : RDataFactory.COMPLETE_VECTOR);
     }
 
-    public static Object typeConvert(RAbstractStringVector x, RAbstractStringVector naStrings, byte asIs, @SuppressWarnings("unused") String numeral) {
+    @Specialization
+    protected Object typeConvert(RAbstractStringVector x, RAbstractStringVector naStrings, byte asIs, @SuppressWarnings("unused") Object dec, @SuppressWarnings("unused") Object numeral) {
         if (x.getLength() == 0) {
             return RDataFactory.createEmptyLogicalVector();
         }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/WriteTable.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/WriteTable.java
index 14dcbc50e4..0709a0ec70 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/WriteTable.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/WriteTable.java
@@ -14,6 +14,7 @@ package com.oracle.truffle.r.library.utils;
 import java.io.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.data.*;
@@ -21,13 +22,11 @@ import com.oracle.truffle.r.runtime.data.model.*;
 
 //Transcribed from GnuR, library/utils/src/io.c
 
-//Checkstyle: stop
-public class WriteTable {
-    // @formatter:off
+public final class WriteTable extends RExternalBuiltinNode {
+
     @TruffleBoundary
-    public static Object execute(RConnection con, Object xx, int nr, int nc, Object rnames, String csep, String ceol, String cna,
-                  char cdec, boolean qmethod, boolean[] quoteCol, boolean quoteRn) throws IOException, IllegalArgumentException {
-        // @formatter:on
+    private static Object execute(RConnection con, Object xx, int nr, int nc, Object rnames, String csep, String ceol, String cna, char cdec, boolean qmethod, boolean[] quoteCol, boolean quoteRn)
+                    throws IOException, IllegalArgumentException {
         OutputStream os = con.getOutputStream();
         String tmp = null;
         if (xx instanceof RDataFrame) { /* A data frame */
@@ -226,4 +225,87 @@ public class WriteTable {
         }
         return false;
     }
+
+    private void invalidArgument(String name) throws RError {
+        errorProfile.enter();
+        throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, name);
+    }
+
+    // Transcribed from GnuR, library/utils/src/io.c
+    @Override
+    public Object call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        Object conArg = argValues[1];
+        RConnection conn;
+        if (!(conArg instanceof RConnection)) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "'file' is not a connection");
+        } else {
+            conn = (RConnection) conArg;
+        }
+        // TODO check connection writeable
+
+        int nr = castInt(castVector(argValues[2]));
+        int nc = castInt(castVector(argValues[3]));
+        Object rnamesArg = argValues[4];
+        Object sepArg = argValues[5];
+        Object eolArg = argValues[6];
+        Object naArg = argValues[7];
+        Object decArg = argValues[8];
+        Object quoteArg = argValues[9];
+        byte qmethod = castLogical(castVector(argValues[10]));
+
+        String csep;
+        String ceol;
+        String cna;
+        String cdec;
+
+        if (nr == RRuntime.INT_NA) {
+            invalidArgument("nr");
+        }
+        if (nc == RRuntime.INT_NA) {
+            invalidArgument("nc");
+        }
+        if (!(rnamesArg instanceof RNull) && isString(rnamesArg) == null) {
+            invalidArgument("rnames");
+        }
+        if ((csep = isString(sepArg)) == null) {
+            invalidArgument("sep");
+        }
+        if ((ceol = isString(eolArg)) == null) {
+            invalidArgument("eol");
+        }
+        if ((cna = isString(naArg)) == null) {
+            invalidArgument("na");
+        }
+        if ((cdec = isString(decArg)) == null) {
+            invalidArgument("dec");
+        }
+        if (qmethod == RRuntime.LOGICAL_NA) {
+            invalidArgument("qmethod");
+        }
+        if (cdec.length() != 1) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "'dec' must be a single character");
+        }
+        boolean[] quoteCol = new boolean[nc];
+        boolean quoteRn = false;
+        RAbstractIntVector quote = (RAbstractIntVector) castVector(quoteArg);
+        for (int i = 0; i < quote.getLength(); i++) {
+            int qi = quote.getDataAt(i);
+            if (qi == 0) {
+                quoteRn = true;
+            }
+            if (qi > 0) {
+                quoteCol[qi - 1] = true;
+            }
+        }
+        try (RConnection openConn = conn.forceOpen("wt")) {
+            execute(openConn, argValues[0], nr, nc, rnamesArg, csep, ceol, cna, cdec.charAt(0), RRuntime.fromLogical(qmethod), quoteCol, quoteRn);
+        } catch (IOException | IllegalArgumentException ex) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage());
+        }
+        return RNull.instance;
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index d5c3a31d32..328873efa0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import com.oracle.truffle.r.nodes.binary.*;
 import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.nodes.builtin.base.foreign.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.ops.*;
 
@@ -226,7 +227,7 @@ public class BasePackage extends RBuiltinPackage {
         add(FileFunctions.ListDirs.class, FileFunctionsFactory.ListDirsNodeGen::create);
         add(FileFunctions.Unlink.class, FileFunctionsFactory.UnlinkNodeGen::create);
         add(Floor.class, Floor::new);
-        add(ForeignFunctions.C.class, ForeignFunctionsFactory.CNodeGen::create);
+        add(DotC.class, DotCNodeGen::create);
         add(ForeignFunctions.DotCall.class, ForeignFunctionsFactory.DotCallNodeGen::create);
         add(ForeignFunctions.DotExternal.class, ForeignFunctionsFactory.DotExternalNodeGen::create);
         add(ForeignFunctions.DotExternal2.class, ForeignFunctionsFactory.DotExternal2NodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java
deleted file mode 100644
index b2477a039b..0000000000
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java
+++ /dev/null
@@ -1,1095 +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-2012, The R Core Team
- * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.nodes.builtin.base;
-
-import java.io.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.utilities.*;
-import com.oracle.truffle.r.library.graphics.*;
-import com.oracle.truffle.r.library.methods.*;
-import com.oracle.truffle.r.library.stats.*;
-import com.oracle.truffle.r.library.tools.*;
-import com.oracle.truffle.r.library.utils.*;
-import com.oracle.truffle.r.nodes.builtin.*;
-import com.oracle.truffle.r.nodes.builtin.base.ForeignFunctionsFactory.DotExternal2NodeGen.ParseRdNodeGen;
-import com.oracle.truffle.r.nodes.unary.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.RError.Message;
-import com.oracle.truffle.r.runtime.conn.*;
-import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.data.model.*;
-import com.oracle.truffle.r.runtime.env.*;
-import com.oracle.truffle.r.runtime.ffi.*;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolInfo;
-
-/**
- * {@code .C}, {@code .Call} {@code .Fortran}, {@code .External}, {@code .External2},
- * {@code External.graphics} functions.
- *
- * TODO Completeness (more types, more error checks), Performance (copying). Especially all the
- * subtleties around copying.
- *
- * See <a href="https://stat.ethz.ch/R-manual/R-devel/library/base/html/Foreign.html">here</a>.
- */
-public class ForeignFunctions {
-    public abstract static class FortranCAdapter extends CastAdapter {
-
-        protected final BranchProfile errorProfile = BranchProfile.create();
-
-        @Override
-        public Object[] getDefaultParameterValues() {
-            return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_FALSE, RMissing.instance, RMissing.instance};
-        }
-
-        protected int[] checkNAs(int argIndex, int[] data) {
-            for (int i = 0; i < data.length; i++) {
-                if (RRuntime.isNA(data[i])) {
-                    errorProfile.enter();
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, argIndex);
-                }
-            }
-            return data;
-        }
-
-        protected double[] checkNAs(int argIndex, double[] data) {
-            for (int i = 0; i < data.length; i++) {
-                if (!RRuntime.isFinite(data[i])) {
-                    errorProfile.enter();
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.NA_NAN_INF_IN_FOREIGN_FUNCTION_CALL, argIndex);
-                }
-            }
-            return data;
-        }
-    }
-
-    /**
-     * For now, just some special case functions that are built in to the implementation.
-     */
-    @RBuiltin(name = ".Fortran", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"})
-    public abstract static class Fortran extends FortranCAdapter {
-        private static final String E = RRuntime.NAMES_ATTR_EMPTY_VALUE;
-        private static final RStringVector DQRDC2_NAMES = RDataFactory.createStringVector(new String[]{"qr", E, E, E, E, "rank", "qraux", "pivot", E}, RDataFactory.COMPLETE_VECTOR);
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "dqrdc2(f)")
-        protected RList fortranDqrdc2(RList f, RArgsValuesAndNames args, byte naok, byte dup, RMissing rPackage, RMissing encoding) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            try {
-                RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
-                int ldx = (int) argValues[1];
-                int n = (int) argValues[2];
-                int p = (int) argValues[3];
-                double tol = (double) argValues[4];
-                RAbstractIntVector rankVec = (RAbstractIntVector) argValues[5];
-                RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[6];
-                RAbstractIntVector pivotVec = (RAbstractIntVector) argValues[7];
-                RAbstractDoubleVector workVec = (RAbstractDoubleVector) argValues[8];
-                double[] x = xVec.materialize().getDataTemp();
-                int[] rank = rankVec.materialize().getDataTemp();
-                double[] qraux = qrauxVec.materialize().getDataTemp();
-                int[] pivot = pivotVec.materialize().getDataTemp();
-                RFFIFactory.getRFFI().getRApplRFFI().dqrdc2(x, ldx, n, p, tol, rank, qraux, pivot, workVec.materialize().getDataCopy());
-                // @formatter:off
-                Object[] data = new Object[]{
-                            RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR, xVec.getDimensions()),
-                            argValues[1], argValues[2], argValues[3], argValues[4],
-                            RDataFactory.createIntVector(rank, RDataFactory.COMPLETE_VECTOR),
-                            RDataFactory.createDoubleVector(qraux, RDataFactory.COMPLETE_VECTOR),
-                            RDataFactory.createIntVector(pivot, RDataFactory.COMPLETE_VECTOR),
-                            argValues[8]
-                };
-                // @formatter:on
-                return RDataFactory.createList(data, DQRDC2_NAMES);
-            } catch (ClassCastException | ArrayIndexOutOfBoundsException ex) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INCORRECT_ARG, "dqrdc2");
-            }
-        }
-
-        public boolean dqrdc2(RList f) {
-            return matchName(f, "dqrdc2");
-        }
-
-        private static final RStringVector DQRCF_NAMES = RDataFactory.createStringVector(new String[]{E, E, E, E, E, E, "coef", "info"}, RDataFactory.COMPLETE_VECTOR);
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "dqrcf(f)")
-        protected RList fortranDqrcf(RList f, RArgsValuesAndNames args, byte naok, byte dup, RMissing rPackage, RMissing encoding) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            try {
-                RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
-                int n = (int) argValues[1];
-                RAbstractIntVector k = (RAbstractIntVector) argValues[2];
-                RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[3];
-                RAbstractDoubleVector yVec = (RAbstractDoubleVector) argValues[4];
-                int ny = (int) argValues[5];
-                RAbstractDoubleVector bVec = (RAbstractDoubleVector) argValues[6];
-                RAbstractIntVector infoVec = (RAbstractIntVector) argValues[7];
-                double[] x = xVec.materialize().getDataTemp();
-                double[] qraux = qrauxVec.materialize().getDataTemp();
-                double[] y = yVec.materialize().getDataTemp();
-                double[] b = bVec.materialize().getDataTemp();
-                int[] info = infoVec.materialize().getDataTemp();
-                RFFIFactory.getRFFI().getRApplRFFI().dqrcf(x, n, k.getDataAt(0), qraux, y, ny, b, info);
-                RDoubleVector coef = RDataFactory.createDoubleVector(b, RDataFactory.COMPLETE_VECTOR);
-                coef.copyAttributesFrom(attrProfiles, bVec);
-                // @formatter:off
-                Object[] data = new Object[]{
-                            RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR),
-                            argValues[1],
-                            k.copy(),
-                            RDataFactory.createDoubleVector(qraux, RDataFactory.COMPLETE_VECTOR),
-                            RDataFactory.createDoubleVector(y, RDataFactory.COMPLETE_VECTOR),
-                            argValues[5],
-                            coef,
-                            RDataFactory.createIntVector(info, RDataFactory.COMPLETE_VECTOR),
-                };
-                // @formatter:on
-                return RDataFactory.createList(data, DQRCF_NAMES);
-
-            } catch (ClassCastException | ArrayIndexOutOfBoundsException ex) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INCORRECT_ARG, "dqrcf");
-            }
-        }
-
-        public boolean dqrcf(RList f) {
-            return matchName(f, "dqrcf");
-        }
-
-    }
-
-    @RBuiltin(name = ".C", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"})
-    public abstract static class C extends FortranCAdapter {
-
-        private static final int SCALAR_DOUBLE = 0;
-        private static final int SCALAR_INT = 1;
-        private static final int SCALAR_LOGICAL = 2;
-        @SuppressWarnings("unused") private static final int SCALAR_STRING = 3;
-        private static final int VECTOR_DOUBLE = 10;
-        private static final int VECTOR_INT = 11;
-        private static final int VECTOR_LOGICAL = 12;
-        @SuppressWarnings("unused") private static final int VECTOR_STRING = 12;
-
-        @SuppressWarnings("unused")
-        @Specialization
-        protected RList c(String f, RArgsValuesAndNames args, byte naok, byte dup, RMissing rPackage, RMissing encoding) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            SymbolInfo symbolInfo = DLL.findSymbolInfo(f, null);
-            if (symbolInfo == null) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.C_SYMBOL_NOT_IN_TABLE, f);
-            }
-            boolean dupArgs = RRuntime.fromLogical(dup);
-            boolean checkNA = RRuntime.fromLogical(naok);
-            // Analyze the args, making copies (ignoring dup for now)
-            int[] argTypes = new int[argValues.length];
-            Object[] nativeArgs = new Object[argValues.length];
-            for (int i = 0; i < argValues.length; i++) {
-                Object arg = argValues[i];
-                if (arg instanceof RAbstractDoubleVector) {
-                    argTypes[i] = VECTOR_DOUBLE;
-                    nativeArgs[i] = checkNAs(i + 1, ((RAbstractDoubleVector) arg).materialize().getDataCopy());
-                } else if (arg instanceof RAbstractIntVector) {
-                    argTypes[i] = VECTOR_INT;
-                    nativeArgs[i] = checkNAs(i + 1, ((RAbstractIntVector) arg).materialize().getDataCopy());
-                } else if (arg instanceof RAbstractLogicalVector) {
-                    argTypes[i] = VECTOR_LOGICAL;
-                    // passed as int[]
-                    byte[] data = ((RAbstractLogicalVector) arg).materialize().getDataWithoutCopying();
-                    int[] dataAsInt = new int[data.length];
-                    for (int j = 0; j < data.length; j++) {
-                        // An NA is an error but the error handling happens in checkNAs
-                        dataAsInt[j] = RRuntime.isNA(data[j]) ? RRuntime.INT_NA : data[j];
-                    }
-                    nativeArgs[i] = checkNAs(i + 1, dataAsInt);
-                } else if (arg instanceof Double) {
-                    argTypes[i] = SCALAR_DOUBLE;
-                    nativeArgs[i] = checkNAs(i + 1, new double[]{(double) arg});
-                } else if (arg instanceof Integer) {
-                    argTypes[i] = SCALAR_INT;
-                    nativeArgs[i] = checkNAs(i + 1, new int[]{(int) arg});
-                } else if (arg instanceof Byte) {
-                    argTypes[i] = SCALAR_LOGICAL;
-                    nativeArgs[i] = checkNAs(i + 1, new int[]{RRuntime.isNA((byte) arg) ? RRuntime.INT_NA : (byte) arg});
-                } else {
-                    errorProfile.enter();
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1);
-                }
-            }
-            try {
-                RFFIFactory.getRFFI().getCRFFI().invoke(symbolInfo, nativeArgs);
-            } catch (Throwable t) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NATIVE_CALL_FAILED, t.getMessage());
-            }
-            // we have to assume that the native method updated everything
-            RStringVector listNames = validateArgNames(argValues.length, getSuppliedSignature());
-            Object[] results = new Object[argValues.length];
-            for (int i = 0; i < argValues.length; i++) {
-                switch (argTypes[i]) {
-                    case SCALAR_DOUBLE:
-                        results[i] = RDataFactory.createDoubleVector((double[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case SCALAR_INT:
-                        results[i] = RDataFactory.createIntVector((int[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case SCALAR_LOGICAL:
-                        results[i] = RDataFactory.createLogicalVector((byte[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
-                        break;
-                    case VECTOR_DOUBLE: {
-                        results[i] = ((RAbstractDoubleVector) argValues[i]).materialize().copyResetData((double[]) nativeArgs[i]);
-                        break;
-                    }
-                    case VECTOR_INT: {
-                        results[i] = ((RAbstractIntVector) argValues[i]).materialize().copyResetData((int[]) nativeArgs[i]);
-                        break;
-                    }
-                    case VECTOR_LOGICAL: {
-                        results[i] = ((RAbstractLogicalVector) argValues[i]).materialize().copyResetData((byte[]) nativeArgs[i]);
-                        break;
-                    }
-
-                }
-            }
-            return RDataFactory.createList(results, listNames);
-        }
-
-        private static RStringVector validateArgNames(int argsLength, ArgumentsSignature signature) {
-            String[] listArgNames = new String[argsLength];
-            for (int i = 0; i < argsLength; i++) {
-                String name = signature.getName(i + 1);
-                if (name == null) {
-                    name = RRuntime.NAMES_ATTR_EMPTY_VALUE;
-                }
-                listArgNames[i] = name;
-            }
-            return RDataFactory.createStringVector(listArgNames, RDataFactory.COMPLETE_VECTOR);
-        }
-
-    }
-
-    /**
-     * Handles the generic case, but also many special case functions that are called from the
-     * default packages.
-     */
-    @RBuiltin(name = ".Call", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
-    public abstract static class DotCall extends CastAdapter {
-
-        private final BranchProfile errorProfile = BranchProfile.create();
-        private final ConditionProfile zVecLgt1 = ConditionProfile.createBinaryProfile();
-        private final ConditionProfile noDims = ConditionProfile.createBinaryProfile();
-
-        @Override
-        public Object[] getDefaultParameterValues() {
-            return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RMissing.instance};
-        }
-
-        // TODO: handle more argument types (this is sufficient to run the b25 benchmarks)
-        @SuppressWarnings("unused")
-        @Specialization(guards = "fft(f)")
-        protected RComplexVector callFFT(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            RComplexVector zVec = castComplexVector(castVector(argValues[0]));
-            double[] z = zVec.getDataTemp();
-            byte inverse = castLogical(castVector(argValues[1]));
-            int inv = RRuntime.isNA(inverse) || inverse == RRuntime.LOGICAL_FALSE ? -2 : 2;
-            int retCode = 7;
-            if (zVecLgt1.profile(zVec.getLength() > 1)) {
-                int[] maxf = new int[1];
-                int[] maxp = new int[1];
-                if (noDims.profile(zVec.getDimensions() == null)) {
-                    int n = zVec.getLength();
-                    RFFIFactory.getRFFI().getStatsRFFI().fft_factor(n, maxf, maxp);
-                    if (maxf[0] == 0) {
-                        errorProfile.enter();
-                        throw RError.error(getEncapsulatingSourceSection(), RError.Message.FFT_FACTORIZATION);
-                    }
-                    double[] work = new double[4 * maxf[0]];
-                    int[] iwork = new int[maxp[0]];
-                    retCode = RFFIFactory.getRFFI().getStatsRFFI().fft_work(z, 1, n, 1, inv, work, iwork);
-                } else {
-                    int maxmaxf = 1;
-                    int maxmaxp = 1;
-                    int[] d = zVec.getDimensions();
-                    int ndims = d.length;
-                    /* do whole loop just for error checking and maxmax[fp] .. */
-                    for (int i = 0; i < ndims; i++) {
-                        if (d[i] > 1) {
-                            RFFIFactory.getRFFI().getStatsRFFI().fft_factor(d[i], maxf, maxp);
-                            if (maxf[0] == 0) {
-                                errorProfile.enter();
-                                throw RError.error(getEncapsulatingSourceSection(), RError.Message.FFT_FACTORIZATION);
-                            }
-                            if (maxf[0] > maxmaxf) {
-                                maxmaxf = maxf[0];
-                            }
-                            if (maxp[0] > maxmaxp) {
-                                maxmaxp = maxp[0];
-                            }
-                        }
-                    }
-                    double[] work = new double[4 * maxmaxf];
-                    int[] iwork = new int[maxmaxp];
-                    int nseg = zVec.getLength();
-                    int n = 1;
-                    int nspn = 1;
-                    for (int i = 0; i < ndims; i++) {
-                        if (d[i] > 1) {
-                            nspn *= n;
-                            n = d[i];
-                            nseg /= n;
-                            RFFIFactory.getRFFI().getStatsRFFI().fft_factor(n, maxf, maxp);
-                            RFFIFactory.getRFFI().getStatsRFFI().fft_work(z, nseg, n, nspn, inv, work, iwork);
-                        }
-                    }
-
-                }
-            }
-
-            return RDataFactory.createComplexVector(z, zVec.isComplete(), zVec.getDimensions());
-        }
-
-        public boolean fft(RList f) {
-            return matchName(f, "fft");
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "initMethodDispatch(f)")
-        @TruffleBoundary
-        protected REnvironment initMethodDispatch(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            REnvironment env = (REnvironment) argValues[0];
-            // TBD what should we actually do here
-            return MethodsListDispatch.getInstance().initMethodDispatch(env);
-        }
-
-        public boolean initMethodDispatch(RList f) {
-            return matchName(f, "R_initMethodDispatch");
-        }
-
-        @SuppressWarnings("unused")
-        @TruffleBoundary
-        @Specialization(guards = "methodsPackageMetaName(f)")
-        protected String callMethodsPackageMetaName(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            // TODO proper error checks
-            String prefixString = (String) argValues[0];
-            String nameString = (String) argValues[1];
-            String pkgString = (String) argValues[2];
-            return MethodsListDispatch.getInstance().methodsPackageMetaName(prefixString, nameString, pkgString);
-        }
-
-        public boolean methodsPackageMetaName(RList f) {
-            return matchName(f, "R_methodsPackageMetaName");
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isSetPrimitiveMethods(f)")
-        @TruffleBoundary
-        protected Object setPrimitiveMethods(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            // TODO proper error checks
-            String fname = RRuntime.asString(argValues[0]);
-            Object op = argValues[1];
-            String codeVec = RRuntime.asString(argValues[2]);
-            RFunction fundef = (RFunction) argValues[3];
-            Object mlist = argValues[4];
-            return SetPrimitiveMethods.doit(fname, op, codeVec, fundef, mlist);
-        }
-
-        public boolean isSetPrimitiveMethods(RList f) {
-            return matchName(f, "R_M_setPrimitiveMethods");
-        }
-
-        @SuppressWarnings("unused")
-        @TruffleBoundary
-        @Specialization(guards = "getClassFromCache(f)")
-        protected Object callGetClassFromCache(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            REnvironment table = (REnvironment) argValues[1];
-            String klassString = RRuntime.asString(argValues[0]);
-            if (klassString != null) {
-                return MethodsListDispatch.getInstance().getClassFromCache(table, klassString);
-            } else {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARG_TYPE);
-            }
-        }
-
-        public boolean getClassFromCache(RList f) {
-            return matchName(f, "R_getClassFromCache");
-        }
-
-        @SuppressWarnings("unused")
-        @TruffleBoundary
-        @Specialization(guards = "setMethodDispatch(f)")
-        protected Object callSetMethodDispatch(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            byte onOff = (byte) args.getArgument(0);
-            return MethodsListDispatch.getInstance().setMethodDispatch(onOff);
-        }
-
-        public boolean setMethodDispatch(RList f) {
-            return matchName(f, "R_set_method_dispatch");
-        }
-
-        @Specialization
-        public Object callNamedFunction(String name, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            return callNamedFunctionWithPackage(name, args, null);
-        }
-
-        @Specialization
-        public Object callNamedFunctionWithPackage(String name, RArgsValuesAndNames args, String packageName) {
-            SymbolInfo symbolInfo = DLL.findSymbolInfo(name, packageName);
-            if (symbolInfo == null) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), Message.C_SYMBOL_NOT_IN_TABLE, name);
-            }
-            try {
-                return RFFIFactory.getRFFI().getCallRFFI().invokeCall(symbolInfo, args.getArguments());
-            } catch (Throwable t) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NATIVE_CALL_FAILED, t.getMessage());
-            }
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isFlushconsole(f)")
-        protected RNull flushConsole(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            return RNull.instance;
-        }
-
-        public boolean isFlushconsole(RList f) {
-            return matchName(f, "flushconsole");
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isCrc64(f)")
-        protected String crc64(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            String input = RRuntime.asString(args.getArgument(0));
-            return Crc64.crc64(input);
-        }
-
-        public boolean isCrc64(RList f) {
-            return matchName(f, "crc64");
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isMenu(f)")
-        @TruffleBoundary
-        protected int menu(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            Object[] values = args.getArguments();
-            String[] choices;
-            if (values[0] instanceof String) {
-                choices = new String[]{(String) values[0]};
-            } else if (values[0] instanceof RAbstractStringVector) {
-                choices = ((RAbstractStringVector) values[0]).materialize().getDataWithoutCopying();
-            } else {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "choices");
-            }
-            return Menu.menu(choices);
-        }
-
-        public boolean isMenu(RList f) {
-            return matchName(f, "menu");
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isCairoProps(f)")
-        protected byte cairoProps(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            return RRuntime.LOGICAL_FALSE;
-        }
-
-        public boolean isCairoProps(RList f) {
-            return matchName(f, "cairoProps");
-        }
-
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isMakeQuartzDefault(f)")
-        protected byte makeQuartzDefault(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            return RRuntime.LOGICAL_FALSE;
-        }
-
-        public boolean isMakeQuartzDefault(RList f) {
-            return matchName(f, "makeQuartzDefault");
-        }
-
-        @Specialization(guards = "isCor(f)")
-        protected Object doCor(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            return doCovCor(false, args);
-        }
-
-        public boolean isCor(RList f) {
-            return matchName(f, "cor");
-        }
-
-        @Specialization(guards = "isCov(f)")
-        protected Object doCov(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            return doCovCor(true, args);
-        }
-
-        public boolean isCov(RList f) {
-            return matchName(f, "cov");
-        }
-
-        private Object doCovCor(boolean isCov, RArgsValuesAndNames args) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            if (argValues[0] == RNull.instance) {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.IS_NULL, "x");
-            }
-            // TODO error checks/coercions
-            RAbstractDoubleVector x = (RAbstractDoubleVector) argValues[0];
-            RAbstractDoubleVector y = argValues[1] == RNull.instance ? null : (RAbstractDoubleVector) argValues[1];
-            int method = ((RAbstractIntVector) argValues[2]).getDataAt(0);
-            if (method != 4) {
-                throw RError.nyi(getEncapsulatingSourceSection(), "method");
-            }
-            boolean iskendall = RRuntime.fromLogical(castLogical(castVector(argValues[3])));
-            return Covcor.getInstance().corcov(x.materialize(), y != null ? y.materialize() : null, method, iskendall, !isCov, getEncapsulatingSourceSection());
-
-        }
-
-        @Specialization(guards = "isSplineCoef(f)")
-        protected RList splineCoef(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            int method = castInt(castVector(args.getArguments()[0]));
-            RAbstractDoubleVector x = (RAbstractDoubleVector) castVector(args.getArguments()[1]);
-            RAbstractDoubleVector y = (RAbstractDoubleVector) castVector(args.getArguments()[2]);
-            return SplineFunctions.splineCoef(method, x.materialize(), y.materialize());
-        }
-
-        public boolean isSplineCoef(RList f) {
-            return matchName(f, "SplineCoef");
-        }
-
-        @Specialization(guards = "isSplineEval(f)")
-        protected RDoubleVector splineEval(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            RAbstractDoubleVector xout = (RAbstractDoubleVector) castVector(args.getArgument(0));
-            // This is called with the result of SplineCoef, so it is surely an RList
-            return SplineFunctions.splineEval(attrProfiles, xout.materialize(), (RList) args.getArgument(1));
-        }
-
-        public boolean isSplineEval(RList f) {
-            return matchName(f, "SplineEval");
-        }
-
-        @Specialization(guards = "isDoTabExpand(f)")
-        protected RStringVector tabExpand(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            RAbstractStringVector strings = (RAbstractStringVector) castVector(args.getArgument(0));
-            RAbstractIntVector starts = (RAbstractIntVector) castVector(args.getArgument(1));
-            return ToolsText.doTabExpand(strings.materialize(), starts.materialize());
-        }
-
-        public boolean isDoTabExpand(RList f) {
-            return matchName(f, "doTabExpand");
-        }
-
-        @Specialization(guards = "isCodeFilesAppend(f)")
-        protected RLogicalVector codeFilesAppend(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            RStringVector file1 = (RStringVector) castVector(args.getArgument(0));
-            RStringVector file2 = (RStringVector) castVector(args.getArgument(1));
-            if (file1.getLength() != 1) {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "file1");
-            }
-            if (file2.getLength() < 1) {
-                return RDataFactory.createEmptyLogicalVector();
-            }
-            return ToolsText.filesAppendLF(file1.getDataAt(0), file2);
-        }
-
-        public boolean isCodeFilesAppend(RList f) {
-            return matchName(f, "codeFilesAppend");
-        }
-
-        @Specialization(guards = "isRmd5(f)")
-        protected RStringVector rmd5(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            RStringVector files = (RStringVector) castVector(args.getArgument(0));
-            return ToolsRmd5.rmd5(files);
-        }
-
-        public boolean isRmd5(RList f) {
-            return matchName(f, "Rmd5");
-        }
-
-        @Specialization(guards = "isDirChmod(f)")
-        protected RNull dirChmod(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            RAbstractVector dir = castVector(args.getArgument(0));
-            if (!(dir instanceof RStringVector && ((RStringVector) dir).getLength() == 1)) {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "dir");
-            }
-            byte gws = castLogical(castVector(args.getArgument(1)));
-            ToolsDirChmod.dirChmod(((RStringVector) dir).getDataAt(0), RRuntime.fromLogical(gws));
-            return RNull.instance;
-        }
-
-        public boolean isDirChmod(RList f) {
-            return matchName(f, "dirchmod");
-        }
-
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object dotCallFallback(Object fobj, Object args, Object packageName) {
-            String name = null;
-            if (fobj instanceof RList) {
-                RList f = (RList) fobj;
-                RStringVector names = f.getNames(attrProfiles);
-                for (int i = 0; i < names.getLength(); i++) {
-                    if (names.getDataAt(i).equals("name")) {
-                        name = (String) f.getDataAt(i);
-                        break;
-                    }
-                }
-            }
-            throw new RInternalError(".Call specialization failure: %s ", name == null ? "<unknown>" : name);
-        }
-
-    }
-
-    private static String isString(Object arg) {
-        if (arg instanceof String) {
-            return (String) arg;
-        } else if (arg instanceof RAbstractStringVector) {
-            if (((RAbstractStringVector) arg).getLength() == 0) {
-                return null;
-            } else {
-                return ((RAbstractStringVector) arg).getDataAt(0);
-            }
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Casts for use on value elements of {@link RArgsValuesAndNames}. Since the starting value
-     * could a scalar, first use {@link #castVector}.
-     */
-    protected abstract static class CastAdapter extends RBuiltinNode {
-        @Child private CastLogicalNode castLogical;
-        @Child private CastIntegerNode castInt;
-        @Child private CastDoubleNode castDouble;
-        @Child private CastComplexNode castComplex;
-        @Child private CastToVectorNode castVector;
-
-        protected final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-        protected byte castLogical(RAbstractVector operand) {
-            if (castLogical == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castLogical = insert(CastLogicalNodeGen.create(null, false, false, false));
-            }
-            return ((RAbstractLogicalVector) castLogical.executeCast(operand)).getDataAt(0);
-        }
-
-        protected int castInt(RAbstractVector operand) {
-            if (castInt == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castInt = insert(CastIntegerNodeGen.create(null, false, false, false));
-            }
-            return ((RAbstractIntVector) castInt.executeCast(operand)).getDataAt(0);
-        }
-
-        protected RAbstractDoubleVector castDouble(RAbstractVector operand) {
-            if (castDouble == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castDouble = insert(CastDoubleNodeGen.create(null, false, false, false));
-            }
-            return (RAbstractDoubleVector) castDouble.executeCast(operand);
-        }
-
-        protected RComplexVector castComplexVector(Object operand) {
-            if (castComplex == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castComplex = insert(CastComplexNodeGen.create(null, true, true, false));
-            }
-            return (RComplexVector) castComplex.executeCast(operand);
-        }
-
-        protected RAbstractVector castVector(Object value) {
-            if (castVector == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castVector = insert(CastToVectorNodeGen.create(null, false, false, false, false));
-            }
-            return (RAbstractVector) castVector.executeObject(value);
-        }
-
-        /**
-         * This is an inefficient guard but it matters little unless there are many different calls
-         * being made within the same evaluation. A {@code NativeSymbolInfo} object would provide a
-         * more efficient basis.
-         */
-        protected boolean matchName(RList f, String name) {
-            if (f.getNames(attrProfiles) == null) {
-                return false;
-            }
-            RAbstractStringVector names = f.getNames(attrProfiles);
-            for (int i = 0; i < names.getLength(); i++) {
-                if (names.getDataAt(i).equals("name")) {
-                    return f.getDataAt(i).equals(name) ? true : false;
-                }
-            }
-            return false;
-        }
-
-    }
-
-    @RBuiltin(name = ".External", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
-    public abstract static class DotExternal extends CastAdapter {
-
-        private final BranchProfile errorProfile = BranchProfile.create();
-
-        // Transcribed from GnuR, library/utils/src/io.c
-        @SuppressWarnings("unused")
-        @Specialization(guards = "isCountFields(f)")
-        protected Object countFields(RList f, RArgsValuesAndNames args, RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            RConnection conn = (RConnection) argValues[0];
-            Object sepArg = argValues[1];
-            char sepChar;
-            Object quoteArg = argValues[2];
-            int nskip = castInt(castVector(argValues[3]));
-            byte blskip = castLogical(castVector(argValues[4]));
-            String commentCharArg = isString(argValues[5]);
-            char comChar;
-            if (!(commentCharArg != null && commentCharArg.length() == 1)) {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "comment.char");
-            } else {
-                comChar = commentCharArg.charAt(0);
-            }
-
-            if (nskip < 0 || nskip == RRuntime.INT_NA) {
-                nskip = 0;
-            }
-            if (blskip == RRuntime.LOGICAL_NA) {
-                blskip = RRuntime.LOGICAL_TRUE;
-            }
-
-            if (sepArg instanceof RNull) {
-                sepChar = 0;
-            } else {
-                String s = isString(sepArg);
-                if (s == null) {
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, "sep");
-                } else {
-                    if (s.length() == 0) {
-                        sepChar = 0;
-                    } else {
-                        sepChar = s.charAt(0);
-                    }
-                }
-            }
-            String quoteSet;
-            if (quoteArg instanceof RNull) {
-                quoteSet = "";
-            } else {
-                String s = isString(quoteArg);
-                if (s == null) {
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "invalid quote symbol set");
-                } else {
-                    quoteSet = s;
-                }
-            }
-            try (RConnection openConn = conn.forceOpen("r")) {
-                return CountFields.execute(openConn, sepChar, quoteSet, nskip, RRuntime.fromLogical(blskip), comChar);
-            } catch (IllegalStateException | IOException ex) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage());
-            }
-        }
-
-        public boolean isCountFields(RList f) {
-            return matchName(f, "countfields");
-        }
-
-        @Specialization(guards = "isReadTableHead(f)")
-        protected Object doReadTableHead(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            // TODO This is quite incomplete and just uses readLines, which works for some inputs
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            RConnection conn = (RConnection) argValues[0];
-            int nlines = castInt(castVector(argValues[1]));
-            try (RConnection openConn = conn.forceOpen("r")) {
-                return RDataFactory.createStringVector(openConn.readLines(nlines), RDataFactory.COMPLETE_VECTOR);
-            } catch (IOException ex) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.ERROR_READING_CONNECTION, ex.getMessage());
-            }
-        }
-
-        public boolean isReadTableHead(RList f) {
-            return matchName(f, "readtablehead");
-        }
-
-        @Specialization(guards = "isRnorm(f)")
-        protected Object doRnorm(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            int n = castInt(castVector(argValues[0]));
-            // TODO full error checks
-            double mean = (double) argValues[1];
-            double standardd = (double) argValues[2];
-            return Random2.rnorm(n, mean, standardd);
-        }
-
-        public boolean isRnorm(RList f) {
-            return matchName(f, "rnorm");
-        }
-
-        @Specialization(guards = "isRunif(f)")
-        protected Object doRunif(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            // TODO full error checks
-            int n = castInt(castVector(argValues[0]));
-            double min = (castDouble(castVector(argValues[1]))).getDataAt(0);
-            double max = (castDouble(castVector(argValues[2]))).getDataAt(0);
-            return Runif.runif(n, min, max);
-        }
-
-        public boolean isRunif(RList f) {
-            return matchName(f, "runif");
-        }
-
-        @Specialization(guards = "isQgamma(f)")
-        protected RAbstractDoubleVector doQgamma(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            RAbstractDoubleVector p = (RAbstractDoubleVector) castVector(argValues[0]);
-            RAbstractDoubleVector shape = (RAbstractDoubleVector) castVector(argValues[1]);
-            RAbstractDoubleVector scale = (RAbstractDoubleVector) castVector(argValues[2]);
-            if (shape.getLength() == 0 || scale.getLength() == 0) {
-                return RDataFactory.createEmptyDoubleVector();
-            }
-            byte lowerTail = castLogical(castVector(argValues[3]));
-            byte logP = castLogical(castVector(argValues[4]));
-            return GammaFunctions.Qgamma.getInstance().qgamma(p, shape, scale, lowerTail, logP, attrProfiles);
-        }
-
-        public boolean isQgamma(RList f) {
-            return matchName(f, "qgamma");
-        }
-
-        @Specialization(guards = "isDownload(f)")
-        protected int doDownload(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            String url = isString(argValues[0]);
-            String destFile = isString(argValues[1]);
-            byte quiet = castLogical(castVector(argValues[2]));
-            String mode = isString(argValues[3]);
-            byte cacheOK = castLogical(castVector(argValues[4]));
-            if (url == null || destFile == null || mode == null) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_UNNAMED_ARGUMENTS);
-            }
-            try {
-                Download.download(url, destFile, RRuntime.fromLogical(quiet), mode, RRuntime.fromLogical(cacheOK));
-                return 0;
-            } catch (IOException ex) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage());
-            }
-        }
-
-        public boolean isDownload(RList f) {
-            return matchName(f, "download");
-        }
-
-    }
-
-    /*
-     * Fully qualified class necessary otherwise javac cannot find the symbol, related to nested
-     * ParseRd class.
-     */
-
-    @com.oracle.truffle.r.runtime.RBuiltin(name = ".External2", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "..."})
-    public abstract static class DotExternal2 extends CastAdapter {
-
-        private final BranchProfile errorProfile = BranchProfile.create();
-
-        // Transcribed from GnuR, library/utils/src/io.c
-        @Specialization(guards = "isWriteTable(f)")
-        protected Object doWriteTable(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            Object conArg = argValues[1];
-            RConnection conn;
-            if (!(conArg instanceof RConnection)) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "'file' is not a connection");
-            } else {
-                conn = (RConnection) conArg;
-            }
-            // TODO check connection writeable
-
-            int nr = castInt(castVector(argValues[2]));
-            int nc = castInt(castVector(argValues[3]));
-            Object rnamesArg = argValues[4];
-            Object sepArg = argValues[5];
-            Object eolArg = argValues[6];
-            Object naArg = argValues[7];
-            Object decArg = argValues[8];
-            Object quoteArg = argValues[9];
-            byte qmethod = castLogical(castVector(argValues[10]));
-
-            String csep;
-            String ceol;
-            String cna;
-            String cdec;
-
-            if (nr == RRuntime.INT_NA) {
-                invalidArgument("nr");
-            }
-            if (nc == RRuntime.INT_NA) {
-                invalidArgument("nc");
-            }
-            if (!(rnamesArg instanceof RNull) && isString(rnamesArg) == null) {
-                invalidArgument("rnames");
-            }
-            if ((csep = isString(sepArg)) == null) {
-                invalidArgument("sep");
-            }
-            if ((ceol = isString(eolArg)) == null) {
-                invalidArgument("eol");
-            }
-            if ((cna = isString(naArg)) == null) {
-                invalidArgument("na");
-            }
-            if ((cdec = isString(decArg)) == null) {
-                invalidArgument("dec");
-            }
-            if (qmethod == RRuntime.LOGICAL_NA) {
-                invalidArgument("qmethod");
-            }
-            if (cdec.length() != 1) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, "'dec' must be a single character");
-            }
-            boolean[] quoteCol = new boolean[nc];
-            boolean quoteRn = false;
-            RAbstractIntVector quote = (RAbstractIntVector) castVector(quoteArg);
-            for (int i = 0; i < quote.getLength(); i++) {
-                int qi = quote.getDataAt(i);
-                if (qi == 0) {
-                    quoteRn = true;
-                }
-                if (qi > 0) {
-                    quoteCol[qi - 1] = true;
-                }
-            }
-            try (RConnection openConn = conn.forceOpen("wt")) {
-                WriteTable.execute(openConn, argValues[0], nr, nc, rnamesArg, csep, ceol, cna, cdec.charAt(0), RRuntime.fromLogical(qmethod), quoteCol, quoteRn);
-            } catch (IOException | IllegalArgumentException ex) {
-                errorProfile.enter();
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.GENERIC, ex.getMessage());
-            }
-            return RNull.instance;
-        }
-
-        protected void invalidArgument(String name) throws RError {
-            errorProfile.enter();
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_ARGUMENT, name);
-        }
-
-        public boolean isWriteTable(RList f) {
-            return matchName(f, "writetable");
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = "isTypeConvert(f)")
-        protected Object doTypeConvert(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args) {
-            controlVisibility();
-            Object[] argValues = args.getArguments();
-            RAbstractStringVector x = (RAbstractStringVector) argValues[0];
-            RAbstractStringVector naStrings = (RAbstractStringVector) argValues[1];
-            byte asIs = (byte) argValues[2];
-            String numeral = RRuntime.asString(argValues[3]);
-            return TypeConvert.typeConvert(x, naStrings, asIs, numeral);
-        }
-
-        public boolean isTypeConvert(RList f) {
-            return matchName(f, "typeconvert");
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = "isPar(f)")
-        protected Object doPar(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args) {
-            controlVisibility();
-            return GraphicsCCalls.par(args);
-        }
-
-        public boolean isPar(RList f) {
-            return matchName(f, "C_par");
-        }
-
-        @Child ParseRdNode parseRdNode;
-
-        @Specialization(guards = "isParseRd(f)")
-        protected Object parseRd(VirtualFrame frame, @SuppressWarnings("unused") RList f, RArgsValuesAndNames args) {
-            if (parseRdNode == null) {
-                parseRdNode = insert(ParseRdNodeGen.create());
-            }
-            Object[] av = args.getArguments();
-            return parseRdNode.execute(frame, av[0], av[1], av[2], av[3], av[4], av[5], av[6]);
-        }
-
-        public abstract static class ParseRdNode extends Node {
-            public abstract Object execute(VirtualFrame frame, Object con, Object srcfile, Object encoding, Object verbose, Object basename, Object fragment, Object warningCalls);
-
-            @Specialization
-            protected Object parseRd(RConnection con, REnvironment srcfile, String encoding, byte verbose, RAbstractStringVector basename, byte fragment, byte warningCalls) {
-                return ToolsParseRd.parseRd(con, srcfile, encoding, RRuntime.fromLogical(verbose), basename, RRuntime.fromLogical(fragment), RRuntime.fromLogical(warningCalls));
-            }
-
-            @SuppressWarnings("unused")
-            @Fallback
-            public Object parseRd(VirtualFrame frame, Object con, Object srcfile, Object encoding, Object verbose, Object basename, Object fragment, Object warningCalls) {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
-            }
-
-        }
-
-        public boolean isParseRd(RList f) {
-            return matchName(f, "C_parseRd");
-        }
-    }
-
-    @RBuiltin(name = ".External.graphics", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "..."})
-    public abstract static class DotExternalGraphics extends CastAdapter {
-        @TruffleBoundary
-        @Specialization(guards = "isPlotXY(f)")
-        protected RNull doPlotXY(@SuppressWarnings("unused") RList f, RArgsValuesAndNames args) {
-            controlVisibility();
-            GraphicsCCalls.plotXy(((RAbstractDoubleVector) args.getArgument(0)).materialize());
-            return RNull.instance;
-        }
-
-        public boolean isPlotXY(RList f) {
-            return matchName(f, "C_plotXY");
-        }
-
-    }
-
-}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CairoProps.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CairoProps.java
new file mode 100644
index 0000000000..3473c73583
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CairoProps.java
@@ -0,0 +1,25 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+public abstract class CairoProps extends RExternalBuiltinNode.Arg1 {
+
+    @Specialization
+    protected byte cairoProps(@SuppressWarnings("unused") RAbstractIntVector param) {
+        return RRuntime.LOGICAL_FALSE;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
new file mode 100644
index 0000000000..9eebca4455
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
@@ -0,0 +1,166 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ffi.*;
+import com.oracle.truffle.r.runtime.ffi.DLL.*;
+
+/**
+ * {@code .C} functions.
+ *
+ * TODO Completeness (more types, more error checks), Performance (copying). Especially all the
+ * subtleties around copying.
+ *
+ * See <a href="https://stat.ethz.ch/R-manual/R-devel/library/base/html/Foreign.html">here</a>.
+ */
+@RBuiltin(name = ".C", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"})
+public abstract class DotC extends RBuiltinNode {
+
+    private static final int SCALAR_DOUBLE = 0;
+    private static final int SCALAR_INT = 1;
+    private static final int SCALAR_LOGICAL = 2;
+    @SuppressWarnings("unused") private static final int SCALAR_STRING = 3;
+    private static final int VECTOR_DOUBLE = 10;
+    private static final int VECTOR_INT = 11;
+    private static final int VECTOR_LOGICAL = 12;
+    @SuppressWarnings("unused") private static final int VECTOR_STRING = 12;
+
+    private final BranchProfile errorProfile = BranchProfile.create();
+
+    @Override
+    public Object[] getDefaultParameterValues() {
+        return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_FALSE, RMissing.instance, RMissing.instance};
+    }
+
+    private int[] checkNAs(int argIndex, int[] data) {
+        for (int i = 0; i < data.length; i++) {
+            if (RRuntime.isNA(data[i])) {
+                errorProfile.enter();
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, argIndex);
+            }
+        }
+        return data;
+    }
+
+    private double[] checkNAs(int argIndex, double[] data) {
+        for (int i = 0; i < data.length; i++) {
+            if (!RRuntime.isFinite(data[i])) {
+                errorProfile.enter();
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NA_NAN_INF_IN_FOREIGN_FUNCTION_CALL, argIndex);
+            }
+        }
+        return data;
+    }
+
+    private static RStringVector validateArgNames(int argsLength, ArgumentsSignature signature) {
+        String[] listArgNames = new String[argsLength];
+        for (int i = 0; i < argsLength; i++) {
+            String name = signature.getName(i + 1);
+            if (name == null) {
+                name = RRuntime.NAMES_ATTR_EMPTY_VALUE;
+            }
+            listArgNames[i] = name;
+        }
+        return RDataFactory.createStringVector(listArgNames, RDataFactory.COMPLETE_VECTOR);
+    }
+
+    @SuppressWarnings("unused")
+    @Specialization
+    protected RList c(String f, RArgsValuesAndNames args, byte naok, byte dup, RMissing rPackage, RMissing encoding) {
+        controlVisibility();
+        Object[] argValues = args.getArguments();
+        SymbolInfo symbolInfo = DLL.findSymbolInfo(f, null);
+        if (symbolInfo == null) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.C_SYMBOL_NOT_IN_TABLE, f);
+        }
+        boolean dupArgs = RRuntime.fromLogical(dup);
+        boolean checkNA = RRuntime.fromLogical(naok);
+        // Analyze the args, making copies (ignoring dup for now)
+        int[] argTypes = new int[argValues.length];
+        Object[] nativeArgs = new Object[argValues.length];
+        for (int i = 0; i < argValues.length; i++) {
+            Object arg = argValues[i];
+            if (arg instanceof RAbstractDoubleVector) {
+                argTypes[i] = VECTOR_DOUBLE;
+                nativeArgs[i] = checkNAs(i + 1, ((RAbstractDoubleVector) arg).materialize().getDataCopy());
+            } else if (arg instanceof RAbstractIntVector) {
+                argTypes[i] = VECTOR_INT;
+                nativeArgs[i] = checkNAs(i + 1, ((RAbstractIntVector) arg).materialize().getDataCopy());
+            } else if (arg instanceof RAbstractLogicalVector) {
+                argTypes[i] = VECTOR_LOGICAL;
+                // passed as int[]
+                byte[] data = ((RAbstractLogicalVector) arg).materialize().getDataWithoutCopying();
+                int[] dataAsInt = new int[data.length];
+                for (int j = 0; j < data.length; j++) {
+                    // An NA is an error but the error handling happens in checkNAs
+                    dataAsInt[j] = RRuntime.isNA(data[j]) ? RRuntime.INT_NA : data[j];
+                }
+                nativeArgs[i] = checkNAs(i + 1, dataAsInt);
+            } else if (arg instanceof Double) {
+                argTypes[i] = SCALAR_DOUBLE;
+                nativeArgs[i] = checkNAs(i + 1, new double[]{(double) arg});
+            } else if (arg instanceof Integer) {
+                argTypes[i] = SCALAR_INT;
+                nativeArgs[i] = checkNAs(i + 1, new int[]{(int) arg});
+            } else if (arg instanceof Byte) {
+                argTypes[i] = SCALAR_LOGICAL;
+                nativeArgs[i] = checkNAs(i + 1, new int[]{RRuntime.isNA((byte) arg) ? RRuntime.INT_NA : (byte) arg});
+            } else {
+                errorProfile.enter();
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1);
+            }
+        }
+        try {
+            RFFIFactory.getRFFI().getCRFFI().invoke(symbolInfo, nativeArgs);
+        } catch (Throwable t) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.NATIVE_CALL_FAILED, t.getMessage());
+        }
+        // we have to assume that the native method updated everything
+        RStringVector listNames = validateArgNames(argValues.length, getSuppliedSignature());
+        Object[] results = new Object[argValues.length];
+        for (int i = 0; i < argValues.length; i++) {
+            switch (argTypes[i]) {
+                case SCALAR_DOUBLE:
+                    results[i] = RDataFactory.createDoubleVector((double[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
+                    break;
+                case SCALAR_INT:
+                    results[i] = RDataFactory.createIntVector((int[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
+                    break;
+                case SCALAR_LOGICAL:
+                    results[i] = RDataFactory.createLogicalVector((byte[]) nativeArgs[i], RDataFactory.COMPLETE_VECTOR);
+                    break;
+                case VECTOR_DOUBLE: {
+                    results[i] = ((RAbstractDoubleVector) argValues[i]).materialize().copyResetData((double[]) nativeArgs[i]);
+                    break;
+                }
+                case VECTOR_INT: {
+                    results[i] = ((RAbstractIntVector) argValues[i]).materialize().copyResetData((int[]) nativeArgs[i]);
+                    break;
+                }
+                case VECTOR_LOGICAL: {
+                    results[i] = ((RAbstractLogicalVector) argValues[i]).materialize().copyResetData((byte[]) nativeArgs[i]);
+                    break;
+                }
+
+            }
+        }
+        return RDataFactory.createList(results, listNames);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
new file mode 100644
index 0000000000..7611b31a35
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
@@ -0,0 +1,64 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ffi.*;
+
+public final class Dqrcf extends RExternalBuiltinNode {
+
+    private static final String E = RRuntime.NAMES_ATTR_EMPTY_VALUE;
+    private static final RStringVector DQRCF_NAMES = RDataFactory.createStringVector(new String[]{E, E, E, E, E, E, "coef", "info"}, RDataFactory.COMPLETE_VECTOR);
+
+    @Override
+    public RList call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        try {
+            RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
+            int n = (int) argValues[1];
+            RAbstractIntVector k = (RAbstractIntVector) argValues[2];
+            RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[3];
+            RAbstractDoubleVector yVec = (RAbstractDoubleVector) argValues[4];
+            int ny = (int) argValues[5];
+            RAbstractDoubleVector bVec = (RAbstractDoubleVector) argValues[6];
+            RAbstractIntVector infoVec = (RAbstractIntVector) argValues[7];
+            double[] x = xVec.materialize().getDataTemp();
+            double[] qraux = qrauxVec.materialize().getDataTemp();
+            double[] y = yVec.materialize().getDataTemp();
+            double[] b = bVec.materialize().getDataTemp();
+            int[] info = infoVec.materialize().getDataTemp();
+            RFFIFactory.getRFFI().getRApplRFFI().dqrcf(x, n, k.getDataAt(0), qraux, y, ny, b, info);
+            RDoubleVector coef = RDataFactory.createDoubleVector(b, RDataFactory.COMPLETE_VECTOR);
+            coef.copyAttributesFrom(attrProfiles, bVec);
+            // @formatter:off
+            Object[] data = new Object[]{
+                        RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR),
+                        argValues[1],
+                        k.copy(),
+                        RDataFactory.createDoubleVector(qraux, RDataFactory.COMPLETE_VECTOR),
+                        RDataFactory.createDoubleVector(y, RDataFactory.COMPLETE_VECTOR),
+                        argValues[5],
+                        coef,
+                        RDataFactory.createIntVector(info, RDataFactory.COMPLETE_VECTOR),
+            };
+            // @formatter:on
+            return RDataFactory.createList(data, DQRCF_NAMES);
+
+        } catch (ClassCastException | ArrayIndexOutOfBoundsException ex) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INCORRECT_ARG, "dqrcf");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
new file mode 100644
index 0000000000..12dd728140
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
@@ -0,0 +1,59 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ffi.*;
+
+public final class Dqrdc2 extends RExternalBuiltinNode {
+
+    private static final String E = RRuntime.NAMES_ATTR_EMPTY_VALUE;
+    private static final RStringVector DQRDC2_NAMES = RDataFactory.createStringVector(new String[]{"qr", E, E, E, E, "rank", "qraux", "pivot", E}, RDataFactory.COMPLETE_VECTOR);
+
+    @Override
+    public RList call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        try {
+            RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
+            int ldx = (int) argValues[1];
+            int n = (int) argValues[2];
+            int p = (int) argValues[3];
+            double tol = (double) argValues[4];
+            RAbstractIntVector rankVec = (RAbstractIntVector) argValues[5];
+            RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[6];
+            RAbstractIntVector pivotVec = (RAbstractIntVector) argValues[7];
+            RAbstractDoubleVector workVec = (RAbstractDoubleVector) argValues[8];
+            double[] x = xVec.materialize().getDataTemp();
+            int[] rank = rankVec.materialize().getDataTemp();
+            double[] qraux = qrauxVec.materialize().getDataTemp();
+            int[] pivot = pivotVec.materialize().getDataTemp();
+            RFFIFactory.getRFFI().getRApplRFFI().dqrdc2(x, ldx, n, p, tol, rank, qraux, pivot, workVec.materialize().getDataCopy());
+            // @formatter:off
+            Object[] data = new Object[]{
+                        RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR, xVec.getDimensions()),
+                        argValues[1], argValues[2], argValues[3], argValues[4],
+                        RDataFactory.createIntVector(rank, RDataFactory.COMPLETE_VECTOR),
+                        RDataFactory.createDoubleVector(qraux, RDataFactory.COMPLETE_VECTOR),
+                        RDataFactory.createIntVector(pivot, RDataFactory.COMPLETE_VECTOR),
+                        argValues[8]
+            };
+            // @formatter:on
+            return RDataFactory.createList(data, DQRDC2_NAMES);
+        } catch (ClassCastException | ArrayIndexOutOfBoundsException ex) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INCORRECT_ARG, "dqrdc2");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
new file mode 100644
index 0000000000..9615e5e6fc
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
@@ -0,0 +1,89 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.ffi.*;
+
+public final class Fft extends RExternalBuiltinNode {
+
+    private final ConditionProfile zVecLgt1 = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile noDims = ConditionProfile.createBinaryProfile();
+
+    // TODO: handle more argument types (this is sufficient to run the b25 benchmarks)
+    @Override
+    public RComplexVector call(RArgsValuesAndNames args) {
+        Object[] argValues = args.getArguments();
+        RComplexVector zVec = castComplexVector(castVector(argValues[0]));
+        double[] z = zVec.getDataTemp();
+        byte inverse = castLogical(castVector(argValues[1]));
+        int inv = RRuntime.isNA(inverse) || inverse == RRuntime.LOGICAL_FALSE ? -2 : 2;
+        @SuppressWarnings("unused")
+        int retCode = 7;
+        if (zVecLgt1.profile(zVec.getLength() > 1)) {
+            int[] maxf = new int[1];
+            int[] maxp = new int[1];
+            if (noDims.profile(zVec.getDimensions() == null)) {
+                int n = zVec.getLength();
+                RFFIFactory.getRFFI().getStatsRFFI().fft_factor(n, maxf, maxp);
+                if (maxf[0] == 0) {
+                    errorProfile.enter();
+                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.FFT_FACTORIZATION);
+                }
+                double[] work = new double[4 * maxf[0]];
+                int[] iwork = new int[maxp[0]];
+                retCode = RFFIFactory.getRFFI().getStatsRFFI().fft_work(z, 1, n, 1, inv, work, iwork);
+            } else {
+                int maxmaxf = 1;
+                int maxmaxp = 1;
+                int[] d = zVec.getDimensions();
+                int ndims = d.length;
+                /* do whole loop just for error checking and maxmax[fp] .. */
+                for (int i = 0; i < ndims; i++) {
+                    if (d[i] > 1) {
+                        RFFIFactory.getRFFI().getStatsRFFI().fft_factor(d[i], maxf, maxp);
+                        if (maxf[0] == 0) {
+                            errorProfile.enter();
+                            throw RError.error(getEncapsulatingSourceSection(), RError.Message.FFT_FACTORIZATION);
+                        }
+                        if (maxf[0] > maxmaxf) {
+                            maxmaxf = maxf[0];
+                        }
+                        if (maxp[0] > maxmaxp) {
+                            maxmaxp = maxp[0];
+                        }
+                    }
+                }
+                double[] work = new double[4 * maxmaxf];
+                int[] iwork = new int[maxmaxp];
+                int nseg = zVec.getLength();
+                int n = 1;
+                int nspn = 1;
+                for (int i = 0; i < ndims; i++) {
+                    if (d[i] > 1) {
+                        nspn *= n;
+                        n = d[i];
+                        nseg /= n;
+                        RFFIFactory.getRFFI().getStatsRFFI().fft_factor(n, maxf, maxp);
+                        RFFIFactory.getRFFI().getStatsRFFI().fft_work(z, nseg, n, nspn, inv, work, iwork);
+                    }
+                }
+
+            }
+        }
+
+        return RDataFactory.createComplexVector(z, zVec.isComplete(), zVec.getDimensions());
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Flushconsole.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Flushconsole.java
new file mode 100644
index 0000000000..3c22499405
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Flushconsole.java
@@ -0,0 +1,23 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.data.*;
+
+public final class Flushconsole extends RExternalBuiltinNode {
+
+    @Override
+    public RNull call(RArgsValuesAndNames args) {
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
new file mode 100644
index 0000000000..897c53403c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -0,0 +1,309 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_Par;
+import com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_PlotXY;
+import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_M_setPrimitiveMethodsNodeGen;
+import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_getClassFromCacheNodeGen;
+import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_initMethodDispatchNodeGen;
+import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_methodsPackageMetaNameNodeGen;
+import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_set_method_dispatchNodeGen;
+import com.oracle.truffle.r.library.stats.*;
+import com.oracle.truffle.r.library.stats.GammaFunctionsFactory.QgammaNodeGen;
+import com.oracle.truffle.r.library.stats.SplineFunctionsFactory.SplineCoefNodeGen;
+import com.oracle.truffle.r.library.stats.SplineFunctionsFactory.SplineEvalNodeGen;
+import com.oracle.truffle.r.library.tools.*;
+import com.oracle.truffle.r.library.tools.ToolsTextFactory.CodeFilesAppendNodeGen;
+import com.oracle.truffle.r.library.tools.ToolsTextFactory.DoTabExpandNodeGen;
+import com.oracle.truffle.r.library.utils.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.ffi.*;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolInfo;
+
+/**
+ * {@code .Call} {@code .Fortran}, {@code .External}, {@code .External2}, {@code External.graphics}
+ * functions.
+ *
+ * TODO Completeness (more types, more error checks), Performance (copying). Especially all the
+ * subtleties around copying.
+ *
+ * See <a href="https://stat.ethz.ch/R-manual/R-devel/library/base/html/Foreign.html">here</a>.
+ */
+public class ForeignFunctions {
+
+    protected abstract static class LookupAdapter extends RBuiltinNode {
+
+        protected abstract RExternalBuiltinNode lookupBuiltin(RList f);
+
+        private static final String UNKNOWN_EXTERNAL_BUILTIN = "UNKNOWN_EXTERNAL_BUILTIN";
+
+        protected String lookupName(RList f) {
+            if (f.getNames() != null) {
+                RAbstractStringVector names = f.getNames();
+                for (int i = 0; i < names.getLength(); i++) {
+                    if (names.getDataAt(i).equals("name")) {
+                        String name = RRuntime.asString(f.getDataAt(i));
+                        return name != null ? name : UNKNOWN_EXTERNAL_BUILTIN;
+                    }
+                }
+            }
+            return UNKNOWN_EXTERNAL_BUILTIN;
+        }
+
+        protected RuntimeException fallback(Object fobj) {
+            String name = null;
+            if (fobj instanceof RList) {
+                name = lookupName((RList) fobj);
+                name = name == UNKNOWN_EXTERNAL_BUILTIN ? null : name;
+                if (name != null && lookupBuiltin((RList) fobj) != null) {
+                    /*
+                     * if we reach this point, then the cache saw a different value for f. the lists
+                     * that contain the information about native calls are never expected to change.
+                     */
+                    throw RInternalError.shouldNotReachHere("fallback reached for " + getRBuiltin().name() + " " + name);
+                }
+            }
+            throw RError.nyi(getEncapsulatingSourceSection(), getRBuiltin().name() + " specialization failure: " + (name == null ? "<unknown>" : name));
+        }
+    }
+
+    /**
+     * For now, just some special case functions that are built in to the implementation.
+     */
+    @RBuiltin(name = ".Fortran", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"})
+    public abstract static class Fortran extends LookupAdapter {
+
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_FALSE, RMissing.instance, RMissing.instance};
+        }
+
+        @Override
+        protected RExternalBuiltinNode lookupBuiltin(RList f) {
+            switch (lookupName(f)) {
+                case "dqrdc2":
+                    return new Dqrdc2();
+                case "dqrcf":
+                    return new Dqrcf();
+                default:
+                    return null;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        @Specialization(limit = "1", guards = {"cached == f", "builtin != null"})
+        protected Object doExternal(RList f, RArgsValuesAndNames args, byte naok, byte dup, RMissing rPackage, RMissing encoding, @Cached("f") RList cached,
+                        @Cached("lookupBuiltin(f)") RExternalBuiltinNode builtin) {
+            controlVisibility();
+            return builtin.call(args);
+        }
+
+        @SuppressWarnings("unused")
+        @Fallback
+        protected Object fallback(Object f, Object args, Object naok, Object dup, Object rPackage, Object encoding) {
+            throw fallback(f);
+        }
+    }
+
+    /**
+     * Handles the generic case, but also many special case functions that are called from the
+     * default packages.
+     */
+    @RBuiltin(name = ".Call", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    public abstract static class DotCall extends LookupAdapter {
+
+        private final BranchProfile errorProfile = BranchProfile.create();
+
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RMissing.instance};
+        }
+
+        @Override
+        protected RExternalBuiltinNode lookupBuiltin(RList f) {
+            switch (lookupName(f)) {
+                case "fft":
+                    return new Fft();
+                case "R_initMethodDispatch":
+                    return R_initMethodDispatchNodeGen.create();
+                case "R_methodsPackageMetaName":
+                    return R_methodsPackageMetaNameNodeGen.create();
+                case "R_set_method_dispatch":
+                    return R_set_method_dispatchNodeGen.create();
+                case "R_M_setPrimitiveMethods":
+                    return R_M_setPrimitiveMethodsNodeGen.create();
+                case "R_getClassFromCache":
+                    return R_getClassFromCacheNodeGen.create();
+                case "crc64":
+                    return Crc64NodeGen.create();
+                case "cov":
+                    return new Covcor(false);
+                case "cor":
+                    return new Covcor(true);
+                case "SplineCoef":
+                    return SplineCoefNodeGen.create();
+                case "SplineEval":
+                    return SplineEvalNodeGen.create();
+                case "doTabExpand":
+                    return DoTabExpandNodeGen.create();
+                case "codeFilesAppend":
+                    return CodeFilesAppendNodeGen.create();
+                case "Rmd5":
+                    return Rmd5NodeGen.create();
+                case "flushconsole":
+                    return new Flushconsole();
+                case "dirchmod":
+                    return DirChmodNodeGen.create();
+                case "cairoProps":
+                    return CairoPropsNodeGen.create();
+                case "makeQuartzDefault":
+                    return new MakeQuartzDefault();
+                default:
+                    return null;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        @Specialization(limit = "1", guards = {"cached == f", "builtin != null"})
+        protected Object doExternal(RList f, RArgsValuesAndNames args, RMissing packageName, @Cached("f") RList cached, @Cached("lookupBuiltin(f)") RExternalBuiltinNode builtin) {
+            controlVisibility();
+            return builtin.call(args);
+        }
+
+        @Specialization
+        public Object callNamedFunction(String name, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) {
+            return callNamedFunctionWithPackage(name, args, null);
+        }
+
+        @Specialization
+        public Object callNamedFunctionWithPackage(String name, RArgsValuesAndNames args, String packageName) {
+            controlVisibility();
+            SymbolInfo symbolInfo = DLL.findSymbolInfo(name, packageName);
+            if (symbolInfo == null) {
+                errorProfile.enter();
+                throw RError.error(getEncapsulatingSourceSection(), Message.C_SYMBOL_NOT_IN_TABLE, name);
+            }
+            try {
+                return RFFIFactory.getRFFI().getCallRFFI().invokeCall(symbolInfo, args.getArguments());
+            } catch (Throwable t) {
+                errorProfile.enter();
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NATIVE_CALL_FAILED, t.getMessage());
+            }
+        }
+
+        @Fallback
+        protected Object dotCallFallback(Object fobj, @SuppressWarnings("unused") Object args, @SuppressWarnings("unused") Object packageName) {
+            throw fallback(fobj);
+        }
+    }
+
+    @RBuiltin(name = ".External", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    public abstract static class DotExternal extends LookupAdapter {
+
+        @Override
+        protected RExternalBuiltinNode lookupBuiltin(RList f) {
+            switch (lookupName(f)) {
+                case "countfields":
+                    return new CountFields();
+                case "readtablehead":
+                    return new ReadTableHead();
+                case "rnorm":
+                    return RnormNodeGen.create();
+                case "runif":
+                    return RunifNodeGen.create();
+                case "qgamma":
+                    return QgammaNodeGen.create();
+                case "download":
+                    return new Download();
+                default:
+                    return null;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        @Specialization(limit = "1", guards = {"cached == f", "builtin != null"})
+        protected Object doExternal(RList f, RArgsValuesAndNames args, RMissing packageName, @Cached("f") RList cached, @Cached("lookupBuiltin(f)") RExternalBuiltinNode builtin) {
+            controlVisibility();
+            return builtin.call(args);
+        }
+
+        @Fallback
+        protected Object fallback(Object f, @SuppressWarnings("unused") Object args, @SuppressWarnings("unused") Object packageName) {
+            throw fallback(f);
+        }
+    }
+
+    @RBuiltin(name = ".External2", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "..."})
+    public abstract static class DotExternal2 extends LookupAdapter {
+
+        @Override
+        protected RExternalBuiltinNode lookupBuiltin(RList f) {
+            switch (lookupName(f)) {
+                case "writetable":
+                    return new WriteTable();
+                case "typeconvert":
+                    return TypeConvertNodeGen.create();
+                case "C_par":
+                    return new C_Par();
+                case "C_parseRd":
+                    return C_ParseRdNodeGen.create();
+                default:
+                    return null;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        @Specialization(limit = "1", guards = {"cached == f", "builtin != null"})
+        protected Object doExternal(RList f, RArgsValuesAndNames args, @Cached("f") RList cached, @Cached("lookupBuiltin(f)") RExternalBuiltinNode builtin) {
+            controlVisibility();
+            return builtin.call(args);
+        }
+
+        @Fallback
+        protected Object fallback(Object f, @SuppressWarnings("unused") Object args) {
+            throw fallback(f);
+        }
+    }
+
+    @RBuiltin(name = ".External.graphics", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "..."})
+    public abstract static class DotExternalGraphics extends LookupAdapter {
+
+        @Override
+        protected RExternalBuiltinNode lookupBuiltin(RList f) {
+            switch (lookupName(f)) {
+                case "C_plotXY":
+                    return new C_PlotXY();
+                default:
+                    return null;
+            }
+        }
+
+        @SuppressWarnings("unused")
+        @Specialization(limit = "1", guards = {"cached == f", "builtin != null"})
+        protected Object doExternal(RList f, RArgsValuesAndNames args, @Cached("f") RList cached, @Cached("lookupBuiltin(f)") RExternalBuiltinNode builtin) {
+            controlVisibility();
+            return builtin.call(args);
+        }
+
+        @Fallback
+        protected Object fallback(Object f, @SuppressWarnings("unused") Object args) {
+            throw fallback(f);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/MakeQuartzDefault.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/MakeQuartzDefault.java
new file mode 100644
index 0000000000..2511c6c372
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/MakeQuartzDefault.java
@@ -0,0 +1,24 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+
+public final class MakeQuartzDefault extends RExternalBuiltinNode {
+
+    @Override
+    public Object call(RArgsValuesAndNames args) {
+        return RRuntime.LOGICAL_FALSE;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java
new file mode 100644
index 0000000000..4ac131f50e
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java
@@ -0,0 +1,36 @@
+/*
+ * 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-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.foreign;
+
+import java.io.*;
+
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.conn.*;
+import com.oracle.truffle.r.runtime.data.*;
+
+public final class ReadTableHead extends RExternalBuiltinNode {
+
+    @Override
+    public Object call(RArgsValuesAndNames args) {
+        // TODO This is quite incomplete and just uses readLines, which works for some inputs
+        Object[] argValues = args.getArguments();
+        RConnection conn = (RConnection) argValues[0];
+        int nlines = castInt(castVector(argValues[1]));
+        try (RConnection openConn = conn.forceOpen("r")) {
+            return RDataFactory.createStringVector(openConn.readLines(nlines), RDataFactory.COMPLETE_VECTOR);
+        } catch (IOException ex) {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.ERROR_READING_CONNECTION, ex.getMessage());
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
new file mode 100644
index 0000000000..c290eade41
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.r.nodes.unary.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@TypeSystemReference(RTypes.class)
+public abstract class RExternalBuiltinNode extends Node {
+
+    public abstract Object call(RArgsValuesAndNames args);
+
+    // TODO: these should be in the build nodes
+    @Child private CastLogicalNode castLogical;
+    @Child private CastIntegerNode castInt;
+    @Child private CastDoubleNode castDouble;
+    @Child private CastComplexNode castComplex;
+    @Child private CastToVectorNode castVector;
+
+    protected final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    protected final BranchProfile errorProfile = BranchProfile.create();
+
+    protected byte castLogical(RAbstractVector operand) {
+        if (castLogical == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castLogical = insert(CastLogicalNodeGen.create(null, false, false, false));
+        }
+        return ((RAbstractLogicalVector) castLogical.executeCast(operand)).getDataAt(0);
+    }
+
+    protected int castInt(RAbstractVector operand) {
+        if (castInt == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castInt = insert(CastIntegerNodeGen.create(null, false, false, false));
+        }
+        return ((RAbstractIntVector) castInt.executeCast(operand)).getDataAt(0);
+    }
+
+    protected RAbstractDoubleVector castDouble(RAbstractVector operand) {
+        if (castDouble == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castDouble = insert(CastDoubleNodeGen.create(null, false, false, false));
+        }
+        return (RAbstractDoubleVector) castDouble.executeCast(operand);
+    }
+
+    protected RComplexVector castComplexVector(Object operand) {
+        if (castComplex == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castComplex = insert(CastComplexNodeGen.create(null, true, true, false));
+        }
+        return (RComplexVector) castComplex.executeCast(operand);
+    }
+
+    protected RAbstractVector castVector(Object value) {
+        if (castVector == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castVector = insert(CastToVectorNodeGen.create(null, false, false, false, false));
+        }
+        return (RAbstractVector) castVector.executeObject(value);
+    }
+
+    protected static String isString(Object arg) {
+        if (arg instanceof String) {
+            return (String) arg;
+        } else if (arg instanceof RAbstractStringVector) {
+            if (((RAbstractStringVector) arg).getLength() == 0) {
+                return null;
+            } else {
+                return ((RAbstractStringVector) arg).getDataAt(0);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    private static void checkLength(RArgsValuesAndNames args, int expectedLength) {
+        if (args.getLength() != expectedLength) {
+            CompilerDirectives.transferToInterpreter();
+            throw RInternalError.shouldNotReachHere("mismatching number of arguments to foreign function");
+        }
+    }
+
+    public abstract static class Arg1 extends RExternalBuiltinNode {
+        public abstract Object execute(Object arg);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 1);
+            return execute(args.getArgument(0));
+        }
+    }
+
+    public abstract static class Arg2 extends RExternalBuiltinNode {
+        public abstract Object execute(Object arg1, Object arg2);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 2);
+            return execute(args.getArgument(0), args.getArgument(1));
+        }
+    }
+
+    public abstract static class Arg3 extends RExternalBuiltinNode {
+        public abstract Object execute(Object arg1, Object arg2, Object arg3);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 3);
+            return execute(args.getArgument(0), args.getArgument(1), args.getArgument(2));
+        }
+    }
+
+    public abstract static class Arg4 extends RExternalBuiltinNode {
+        public abstract Object execute(Object arg1, Object arg2, Object arg3, Object arg4);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 4);
+            return execute(args.getArgument(0), args.getArgument(1), args.getArgument(2), args.getArgument(3));
+        }
+    }
+
+    public abstract static class Arg5 extends RExternalBuiltinNode {
+        public abstract Object execute(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 5);
+            return execute(args.getArgument(0), args.getArgument(1), args.getArgument(2), args.getArgument(3), args.getArgument(4));
+        }
+    }
+
+    public abstract static class Arg6 extends RExternalBuiltinNode {
+        public abstract Object execute(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 6);
+            return execute(args.getArgument(0), args.getArgument(1), args.getArgument(2), args.getArgument(3), args.getArgument(4), args.getArgument(5));
+        }
+    }
+
+    public abstract static class Arg7 extends RExternalBuiltinNode {
+
+        public abstract Object execute(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7);
+
+        @Override
+        public final Object call(RArgsValuesAndNames args) {
+            checkLength(args, 7);
+            return execute(args.getArgument(0), args.getArgument(1), args.getArgument(2), args.getArgument(3), args.getArgument(4), args.getArgument(5), args.getArgument(6));
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
index c56c50c415..b06b98384b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
@@ -484,6 +484,12 @@ public final class RContext extends ExecutionContext {
      */
     @CompilationFinal private static final ThreadLocal<RContext> threadLocalContext = new ThreadLocal<>();
 
+    /**
+     * Used by the MethodListDispatch class.
+     */
+
+    private boolean methodTableDispatchOn = true;
+
     /*
      * Primarily for debugging.
      */
@@ -754,6 +760,14 @@ public final class RContext extends ExecutionContext {
         resultVisible = v;
     }
 
+    public boolean isMethodTableDispatchOn() {
+        return methodTableDispatchOn;
+    }
+
+    public void setMethodTableDispatchOn(boolean on) {
+        methodTableDispatchOn = on;
+    }
+
     public boolean isInteractive() {
         return interactive;
     }
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 9f4a93f505..50ad0096e0 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -7,9 +7,10 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsLis
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/GammaFunctions.java,gnu_r_qgamma.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Random2.java,gnu_r.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java,gnu_r_splines.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/StatsUtil.java,gnu_r_ihaka.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsDirChmod.java,gnu_r.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java,gnu_r.copyright
 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/Menu.java,gnu_r.copyright
@@ -59,7 +60,15 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/D
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java,purdue.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodeString.java,purdue.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java,gnu_r.copyright
-com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CairoProps.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Flushconsole.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/MakeQuartzDefault.java,gnu_r.copyright
+com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ReadTableHead.java,gnu_r.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java,gnu_r.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java,gnu_r.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java,purdue.copyright
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index 7c221ef226..51f0d3b8c7 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -362,8 +362,12 @@ suite = {
     "com.oracle.truffle.r.library" : {
       "sourceDirs" : ["src"],
       "dependencies" : [
+        "com.oracle.truffle.r.nodes",
         "com.oracle.truffle.r.runtime",
       ],
+      "annotationProcessors" : [
+          "com.oracle.truffle.dsl.processor",
+      ],
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "FastR",
-- 
GitLab