From 6c7cd191d66c8fbfc6b3ea4ad01e491d85b000cd Mon Sep 17 00:00:00 2001
From: Tomas Stupka <tomas.stupka@oracle.com>
Date: Fri, 10 Feb 2017 13:39:39 +0100
Subject: [PATCH] added cast pipelines to SubstituteDirect, Substitute,
 TypeConvert, Dqrcf, SplineCoef buildins

---
 .../r/library/methods/SubstituteDirect.java   | 41 ++++++++--
 .../r/library/stats/SplineFunctions.java      | 56 ++++++++++++--
 .../truffle/r/library/utils/TypeConvert.java  | 23 +++++-
 .../r/nodes/builtin/base/Substitute.java      | 33 ++++++--
 .../r/nodes/builtin/base/foreign/Dqrcf.java   | 37 ++++-----
 .../base/foreign/FortranAndCFunctions.java    |  2 +-
 .../r/nodes/builtin/RList2EnvNode.java        | 11 ++-
 .../com/oracle/truffle/r/runtime/RError.java  |  3 +
 .../test/builtins/TestBuiltin_substitute.java | 25 +++++++
 .../library/methods/TestSubstituteDirect.java | 75 +++++++++++++++++++
 .../library/stats/TestSplineFunctions.java    | 71 ++++++++++++++++++
 .../r/test/library/utils/TestTypeConvert.java | 37 +++++++++
 mx.fastr/mx_fastr.py                          |  2 +-
 13 files changed, 370 insertions(+), 46 deletions(-)
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/methods/TestSubstituteDirect.java
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestSplineFunctions.java

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
index 420e634f2e..e1a2b1bac6 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/SubstituteDirect.java
@@ -27,19 +27,24 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.RASTUtils;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.runtime.RError;
+import static com.oracle.truffle.r.runtime.RError.Message.INVALID_LIST_FOR_SUBSTITUTION;
+import static com.oracle.truffle.r.runtime.RError.SHOW_CALLER;
 import com.oracle.truffle.r.runtime.RSubstitute;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 public abstract class SubstituteDirect extends RExternalBuiltinNode.Arg2 {
 
     static {
-        Casts.noCasts(SubstituteDirect.class);
+        Casts casts = new Casts(SubstituteDirect.class);
+        casts.arg(1).defaultError(SHOW_CALLER, INVALID_LIST_FOR_SUBSTITUTION).mustBe(instanceOf(RAbstractListVector.class).or(instanceOf(REnvironment.class)));
     }
 
     @Specialization
@@ -53,18 +58,40 @@ public abstract class SubstituteDirect extends RExternalBuiltinNode.Arg2 {
         }
     }
 
-    @Specialization
+    @Specialization(guards = {"list.getNames() == null || list.getNames().getLength() == 0"})
+    @TruffleBoundary
+    protected static Object substituteDirect(Object object, RList list) {
+        return substituteDirect(object, createNewEnvironment());
+    }
+
+    @Specialization(guards = {"list.getNames() != null", "list.getNames().getLength() > 0"})
     @TruffleBoundary
     protected static Object substituteDirect(Object object, RList list,
-                    @Cached("new()") RList2EnvNode list2Env) {
-        REnvironment env = RDataFactory.createNewEnv(null);
-        env.setParent(REnvironment.baseEnv());
-        list2Env.execute(list, env);
-        return substituteDirect(object, env);
+                    @Cached("createList2EnvNode()") RList2EnvNode list2Env) {
+        return substituteDirect(object, createEnvironment(list, list2Env));
     }
 
     @Fallback
     protected Object substituteDirect(@SuppressWarnings("unused") Object object, @SuppressWarnings("unused") Object env) {
         throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
     }
+
+    @TruffleBoundary
+    public static REnvironment createNewEnvironment() {
+        return createEnvironment(null, null);
+    }
+
+    @TruffleBoundary
+    public static REnvironment createEnvironment(RList list, RList2EnvNode list2Env) {
+        REnvironment env = RDataFactory.createNewEnv(null);
+        env.setParent(REnvironment.baseEnv());
+        if (list2Env != null) {
+            list2Env.execute(list, env);
+        }
+        return env;
+    }
+
+    protected static RList2EnvNode createList2EnvNode() {
+        return new RList2EnvNode(true);
+    }
 }
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 a16c52363c..d6b11cfc74 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
@@ -13,12 +13,22 @@ package com.oracle.truffle.r.library.stats;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asIntegerVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.chain;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.findFirst;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
+import static com.oracle.truffle.r.runtime.RError.Message.NA_INTRODUCED_COERCION;
+import static com.oracle.truffle.r.runtime.RError.NO_CALLER;
+import static com.oracle.truffle.r.runtime.RRuntime.INT_NA;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.nmath.RMath;
@@ -32,12 +42,38 @@ import com.oracle.truffle.r.runtime.nmath.RMath;
 public class SplineFunctions {
 
     public abstract static class SplineCoef extends RExternalBuiltinNode.Arg3 {
+        static {
+            Casts casts = new Casts(SplineCoef.class);
+            casts.arg(0).mapNull(constant(INT_NA)).mapIf(numericValue(),
+                            chain(asIntegerVector()).with(findFirst().integerElement(INT_NA)).end(),
+                            chain(asIntegerVector()).with(findFirst().integerElement(INT_NA)).with(
+                                            Predef.shouldBe(integerValue(), NO_CALLER, NA_INTRODUCED_COERCION)).end());
+            casts.arg(1).allowMissing().asDoubleVector();
+            casts.arg(2).allowMissing().asDoubleVector();
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object splineCoef(int method, RAbstractDoubleVector x, RAbstractDoubleVector y) {
+            return SplineFunctions.splineCoef(method, x.materialize(), y.materialize());
+        }
 
         @Specialization
         @TruffleBoundary
-        protected Object splineCoef(Object method, RAbstractDoubleVector x, RAbstractDoubleVector y) {
-            int methodInt = castInt(castVector(method));
-            return SplineFunctions.splineCoef(methodInt, x.materialize(), y.materialize());
+        protected Object splineCoef(int method, RAbstractDoubleVector x, RNull y) {
+            return SplineFunctions.splineCoef(method, x.materialize(), RDataFactory.createDoubleVector(0));
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object splineCoef(int method, RNull x, RAbstractDoubleVector y) {
+            return SplineFunctions.splineCoef(method, RDataFactory.createDoubleVector(0), y.materialize());
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object splineCoef(int method, RNull x, RNull y) {
+            return SplineFunctions.splineCoef(method, RDataFactory.createDoubleVector(0), RDataFactory.createDoubleVector(0));
         }
     }
 
@@ -99,7 +135,9 @@ public class SplineFunctions {
 
         double[] e = new double[n];
 
-        RInternalError.guarantee(n >= 2 && y[0] == y[n - 1], "periodic spline: domain error");
+        if (n < 2 || y[0] != y[n - 1]) {
+            return;
+        }
 
         if (n == 2) {
             b[0] = 0.0;
@@ -215,7 +253,9 @@ public class SplineFunctions {
         int i;
         double t;
 
-        RInternalError.guarantee(n >= 2, "periodic spline: domain error");
+        if (n < 2) {
+            return;
+        }
 
         if (n < 3) {
             t = (y[1] - y[0]);
@@ -289,7 +329,9 @@ public class SplineFunctions {
         int i;
         double t;
 
-        RInternalError.guarantee(n >= 2, "periodic spline: domain error");
+        if (n < 2) {
+            return;
+        }
 
         if (n < 3) {
             t = (y[1] - y[0]);
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 fb7fbf6c6a..27d158bbc8 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
@@ -27,8 +27,20 @@ import java.util.TreeSet;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asLogicalVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.chain;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.findFirst;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.map;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import static com.oracle.truffle.r.runtime.RError.Message.FIRST_ARGUMENT_MUST_BE_CHARACTER;
+import static com.oracle.truffle.r.runtime.RError.Message.INVALID_ARG;
+import static com.oracle.truffle.r.runtime.RError.SHOW_CALLER;
 import com.oracle.truffle.r.runtime.RRuntime;
+import static com.oracle.truffle.r.runtime.RRuntime.LOGICAL_FALSE;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -41,7 +53,12 @@ public abstract class TypeConvert extends RExternalBuiltinNode.Arg5 {
     @Child private SetFixedAttributeNode setLevelsAttrNode = SetFixedAttributeNode.create(RRuntime.LEVELS_ATTR_KEY);
 
     static {
-        Casts.noCasts(TypeConvert.class);
+        Casts casts = new Casts(TypeConvert.class);
+        casts.arg(0).mustBe(stringValue(), SHOW_CALLER, FIRST_ARGUMENT_MUST_BE_CHARACTER).asStringVector();
+        casts.arg(1).mustBe(stringValue(), SHOW_CALLER, INVALID_ARG, "'na.strings'").asStringVector();
+        casts.arg(2).mapIf(logicalValue(),
+                        chain(asLogicalVector()).with(findFirst().logicalElement(LOGICAL_FALSE)).with(toBoolean()).end(),
+                        chain(map(constant(LOGICAL_FALSE))).with(toBoolean()).end());
     }
 
     private static boolean isNA(String s, RAbstractStringVector naStrings) {
@@ -109,7 +126,7 @@ public abstract class TypeConvert extends RExternalBuiltinNode.Arg5 {
     }
 
     @Specialization
-    protected Object typeConvert(RAbstractStringVector x, RAbstractStringVector naStrings, byte asIs, @SuppressWarnings("unused") Object dec, @SuppressWarnings("unused") Object numeral) {
+    protected Object typeConvert(RAbstractStringVector x, RAbstractStringVector naStrings, boolean asIs, @SuppressWarnings("unused") Object dec, @SuppressWarnings("unused") Object numeral) {
         if (x.getLength() == 0) {
             return RDataFactory.createEmptyLogicalVector();
         }
@@ -164,7 +181,7 @@ public abstract class TypeConvert extends RExternalBuiltinNode.Arg5 {
         }
         // fall through target - conversion to int, double or logical failed
 
-        if (asIs == RRuntime.LOGICAL_TRUE) {
+        if (asIs) {
             return x;
         } else {
             // create a factor
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
index 395271e222..8f75d38052 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
@@ -26,20 +26,25 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.library.methods.SubstituteDirect;
 import com.oracle.truffle.r.nodes.RASTUtils;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
-import com.oracle.truffle.r.runtime.RError;
+import static com.oracle.truffle.r.runtime.RError.Message.INVALID_ENVIRONMENT_SPECIFIED;
 import com.oracle.truffle.r.runtime.RSubstitute;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 @RBuiltin(name = "substitute", kind = PRIMITIVE, parameterNames = {"expr", "env"}, nonEvalArgs = 0, behavior = COMPLEX)
@@ -48,7 +53,8 @@ public abstract class Substitute extends RBuiltinNode {
     @Child private Quote quote;
 
     static {
-        Casts.noCasts(Substitute.class);
+        Casts casts = new Casts(Substitute.class);
+        casts.arg(1).allowNullAndMissing().defaultError(INVALID_ENVIRONMENT_SPECIFIED).mustBe(instanceOf(RAbstractListVector.class).or(instanceOf(REnvironment.class)));
     }
 
     @Specialization
@@ -56,19 +62,25 @@ public abstract class Substitute extends RBuiltinNode {
         return doSubstituteWithEnv(expr, REnvironment.frameToEnvironment(frame.materialize()));
     }
 
+    @Specialization
+    protected Object doSubstitute(VirtualFrame frame, RPromise expr, @SuppressWarnings("unused") RNull env) {
+        return doSubstituteWithEnv(expr, REnvironment.frameToEnvironment(frame.materialize()));
+    }
+
     @Specialization
     protected Object doSubstitute(RPromise expr, REnvironment env) {
         return doSubstituteWithEnv(expr, env);
     }
 
-    @Specialization
+    @Specialization(guards = {"list.getNames() == null || list.getNames().getLength() == 0"})
     protected Object doSubstitute(RPromise expr, RList list) {
-        return doSubstituteWithEnv(expr, REnvironment.createFromList(list, REnvironment.baseEnv()));
+        return doSubstituteWithEnv(expr, SubstituteDirect.createNewEnvironment());
     }
 
-    @Fallback
-    protected Object doSubstitute(@SuppressWarnings("unused") Object expr, @SuppressWarnings("unused") Object x) {
-        throw RError.error(this, RError.Message.INVALID_ENVIRONMENT);
+    @Specialization(guards = {"list.getNames() != null", "list.getNames().getLength() > 0"})
+    protected Object doSubstitute(RPromise expr, RList list,
+                    @Cached("createList2EnvNode()") RList2EnvNode list2Env) {
+        return doSubstituteWithEnv(expr, SubstituteDirect.createEnvironment(list, list2Env));
     }
 
     /**
@@ -99,4 +111,9 @@ public abstract class Substitute extends RBuiltinNode {
         // so get the actual expression (AST) from that
         return RASTUtils.createLanguageElement(RSubstitute.substitute(env, expr.getRep()));
     }
+
+    protected static RList2EnvNode createList2EnvNode() {
+        return new RList2EnvNode(true);
+    }
+
 }
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
index 6941258c6b..d10efffa16 100644
--- 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
@@ -11,10 +11,12 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
+import com.oracle.truffle.api.dsl.Specialization;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -24,44 +26,43 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
-public final class Dqrcf extends RExternalBuiltinNode {
+public abstract class Dqrcf extends RExternalBuiltinNode.Arg8 {
     @Child private RApplRFFI.RApplRFFINode rApplRFFINode = RFFIFactory.getRFFI().getRApplRFFI().createRApplRFFINode();
 
     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);
 
     static {
-        Casts.noCasts(Dqrcf.class);
+        Casts casts = new Casts(Dqrcf.class);
+        casts.arg(0).mustBe(doubleValue()).asDoubleVector();
+        casts.arg(1).mustBe(integerValue()).asIntegerVector().findFirst();
+        casts.arg(2).mustBe(integerValue()).asIntegerVector().findFirst();
+        casts.arg(3).mustBe(doubleValue()).asDoubleVector();
+        casts.arg(4).mustBe(doubleValue()).asDoubleVector();
+        casts.arg(5).mustBe(integerValue()).asIntegerVector().findFirst();
+        casts.arg(6).mustBe(doubleValue()).asDoubleVector();
+        casts.arg(7).mustBe(integerValue()).asIntegerVector();
     }
 
-    @Override
-    public RList call(RArgsValuesAndNames args) {
-        Object[] argValues = args.getArguments();
+    @Specialization
+    public RList dqrcf(RAbstractDoubleVector xVec, int nx, int k, RAbstractDoubleVector qrauxVec, RAbstractDoubleVector yVec, int ny, RAbstractDoubleVector bVec, RAbstractIntVector infoVec) {
         try {
-            RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
-            int n = argValues[1] instanceof Integer ? (int) argValues[1] : ((RAbstractIntVector) argValues[1]).getDataAt(0);
-            RAbstractIntVector k = (RAbstractIntVector) argValues[2];
-            RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[3];
-            RAbstractDoubleVector yVec = (RAbstractDoubleVector) argValues[4];
-            int ny = argValues[5] instanceof Integer ? (int) argValues[5] : ((RAbstractIntVector) argValues[5]).getDataAt(0);
-            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();
-            rApplRFFINode.dqrcf(x, n, k.getDataAt(0), qraux, y, ny, b, info);
+            rApplRFFINode.dqrcf(x, nx, k, qraux, y, ny, b, info);
             RDoubleVector coef = RDataFactory.createDoubleVector(b, RDataFactory.COMPLETE_VECTOR);
             coef.copyAttributesFrom(bVec);
             // @formatter:off
             Object[] data = new Object[]{
                         RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR),
-                        argValues[1],
-                        k.copy(),
+                        nx,
+                        k,
                         RDataFactory.createDoubleVector(qraux, RDataFactory.COMPLETE_VECTOR),
                         RDataFactory.createDoubleVector(y, RDataFactory.COMPLETE_VECTOR),
-                        argValues[5],
+                        ny,
                         coef,
                         RDataFactory.createIntVector(info, RDataFactory.COMPLETE_VECTOR),
             };
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
index 29a1c389bc..d3d6625fa2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
@@ -203,7 +203,7 @@ public class FortranAndCFunctions {
                 case "dqrdc2":
                     return Dqrdc2.create();
                 case "dqrcf":
-                    return new Dqrcf();
+                    return DqrcfNodeGen.create();
                 default:
                     return null;
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java
index a50458a51a..cc2264b97c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RList2EnvNode.java
@@ -33,6 +33,15 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  * Abstracted for use by other nodes that need to convert a list into an environment.
  */
 public final class RList2EnvNode extends RBaseNode {
+    private final boolean ignoreMissingNames;
+
+    public RList2EnvNode() {
+        this(false);
+    }
+
+    public RList2EnvNode(boolean ignoreMissingNames) {
+        this.ignoreMissingNames = ignoreMissingNames;
+    }
 
     @TruffleBoundary
     public REnvironment execute(RAbstractListVector list, REnvironment env) {
@@ -45,7 +54,7 @@ public final class RList2EnvNode extends RBaseNode {
         }
         for (int i = list.getLength() - 1; i >= 0; i--) {
             String name = names.getDataAt(i);
-            if (name.length() == 0) {
+            if (!ignoreMissingNames && name.length() == 0) {
                 throw RError.error(this, RError.Message.ZERO_LENGTH_VARIABLE);
             }
             // in case of duplicates, last element in list wins
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 9f4d41c7fe..58bc36b5d7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -409,6 +409,7 @@ public final class RError extends RuntimeException {
         PROMISE_CYCLE("promise already under evaluation: recursive default argument reference or earlier problems?"),
         MISSING_ARGUMENTS("'missing' can only be used for arguments"),
         INVALID_ENVIRONMENT("invalid environment"),
+        INVALID_ENVIRONMENT_SPECIFIED("invalid environment specified"),
         ENVIR_NOT_LENGTH_ONE("numeric 'envir' arg not of length one"),
         FMT_NOT_CHARACTER("'fmt' is not a character vector"),
         UNSUPPORTED_TYPE("unsupported type"),
@@ -559,6 +560,7 @@ public final class RError extends RuntimeException {
         INVALID_LOGICAL("'%s' must be TRUE or FALSE"),
         INVALID_FORMAT_STRING("invalid format '%s'; use format %%s for character objects"),
         MUST_BE_CHARACTER("'%s' must be of mode character"),
+        FIRST_ARGUMENT_MUST_BE_CHARACTER("the first argument must be of mode character"),
         ALL_ATTRIBUTES_NAMES("all attributes must have names [%d does not]"),
         INVALID_REGEXP("invalid regular expression '%s'"),
         COERCING_ARGUMENT("coercing argument of type '%s' to %s"),
@@ -598,6 +600,7 @@ public final class RError extends RuntimeException {
         NOT_A_SYMBOL("not a symbol"),
         CANNOT_SET_PARENT("cannot set the parent of the empty environment"),
         INVALID_OR_UNIMPLEMENTED_ARGUMENTS("invalid or unimplemented arguments"),
+        INVALID_LIST_FOR_SUBSTITUTION("invalid list for substitution"),
         NOTHING_TO_LINK("nothing to link"),
         FROM_TO_DIFFERENT("'from' and 'to' are of different lengths"),
         NA_IN_FOREIGN_FUNCTION_CALL("NAs in foreign function call (arg %d)"),
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java
index 871968ec0b..ad2e9940d3 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_substitute.java
@@ -87,5 +87,30 @@ public class TestBuiltin_substitute extends TestBase {
         assertEval("f<-function(x,name) substitute(x$name <- 5); f(foo, bar); foo <- new.env(); eval(f(foo,bar)); foo$bar");
         assertEval("f<-function(x,name) substitute(x@name); f(foo, bar); setClass('cl', representation(bar='numeric')); foo <- new('cl'); foo@bar <- 1; eval(f(foo,bar))");
         assertEval("f<-function(x,name) substitute(x@name <- 5); f(foo, bar); setClass('cl', representation(bar='numeric')); foo <- new('cl'); eval(f(foo,bar)); foo@bar");
+
+        assertEval("substitute(1, 1)");
+        assertEval("substitute(1, 1, 1)");
+        assertEval("substitute(expr=1, env=1)");
+
+        assertEval("substitute(1, NULL)");
+        assertEval("substitute(1, NA)");
+        assertEval("substitute(1, c(list(1)))");
+        assertEval("substitute(1, list(c(list(1))))");
+        assertEval("substitute(1, list(list(1)))");
+
+        assertEval("a<-substitute(quote(x+1), NULL); a");
+        assertEval("a<-substitute(quote(x+1), NA); a");
+        assertEval("a<-substitute(quote(x+1), list(1)); a");
+        assertEval("a<-substitute(quote(x+1), list(x=1)); a");
+        assertEval("a<-substitute(quote(x+1), list(y=1)); a");
+        assertEval("a<-substitute(quote(x+1), c(list(x=1), 'breakme')); a");
+        assertEval("a<-substitute(quote(x+1), c(c(list(x=1)))); a");
+        assertEval("a<-substitute(quote(x+1), list(c(c(list(x=1))))); a");
+        assertEval("a<-substitute(quote(x+1), list(list(x=1))); a");
+        assertEval("a<-substitute(quote(x+1), c(list(x=1, 1))); a");
+        assertEval("a<-substitute(quote(x+y), c(list(x=1), list(y=1))); a");
+        assertEval("substitute(quote(x+1), environment())");
+        assertEval("f<-function() {}; substitute(quote(x+1), f)");
+        assertEval("substitute(quote(x+1), setClass('a'))");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/methods/TestSubstituteDirect.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/methods/TestSubstituteDirect.java
new file mode 100644
index 0000000000..4ab7f98157
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/methods/TestSubstituteDirect.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.test.library.methods;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestSubstituteDirect extends TestBase {
+
+    @Test
+    public void basicTests() {
+        assertEval("substituteDirect(NULL, list(x=1))");
+        assertEval("substituteDirect(NA, list(x=1))");
+        assertEval("substituteDirect(environment(), list(x=1))");
+        assertEval("substituteDirect(1, 1)");
+        assertEval("substituteDirect(1, 1, 1)");
+        assertEval("substituteDirect(object=1, frame=1)");
+        assertEval("substituteDirect(object=1, frame=1, cleanFunction=1)");
+
+        assertEval("substituteDirect(1, frame=NULL)");
+        assertEval("substituteDirect(1, frame=NA)");
+        assertEval("substituteDirect(object=1, frame=c(list(1)))");
+        assertEval("substituteDirect(object=1, frame=list(c(list(1))))");
+        assertEval("substituteDirect(object=1, frame=list(list(1)))");
+
+        assertEval("a<-substituteDirect(quote(x+1), NULL); a");
+        assertEval("a<-substituteDirect(quote(x+1), NA); a");
+        assertEval("a<-substituteDirect(quote(x+1), list(1)); a");
+        assertEval("a<-substituteDirect(quote(x+1), list(x=1)); a");
+        assertEval("a<-substituteDirect(quote(x+1), list(y=1)); a");
+        assertEval("a<-substituteDirect(quote(x+1), c(list(x=1), 'breakme')); a");
+        assertEval("a<-substituteDirect(quote(x+1), c(c(list(x=1)))); a");
+        assertEval("a<-substituteDirect(quote(x+1), list(c(c(list(x=1))))); a");
+        assertEval("a<-substituteDirect(quote(x+1), list(list(x=1))); a");
+        assertEval("a<-substituteDirect(quote(x+1), c(list(x=1, 1))); a");
+        assertEval("a<-substituteDirect(quote(x+y), c(list(x=1), list(y=1))); a");
+        assertEval("substituteDirect(quote(x+1), frame=environment())");
+        assertEval("f<-function() {}; substituteDirect(quote(x+1), frame=f)");
+        assertEval("substituteDirect(quote(x+1), frame=setClass('a'))");
+
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=TRUE)");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=c(TRUE, 'breakme'))");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=c(c(TRUE, 'breakme')))");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=c(TRUE, FALSE))");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=list(c(TRUE), 'breakme'))");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=NA)");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=NULL)");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction='a')");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=c('1'))");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=c(1))");
+        assertEval("substituteDirect(quote(x+1), list(x=1), cleanFunction=environment())");
+    }
+
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestSplineFunctions.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestSplineFunctions.java
new file mode 100644
index 0000000000..defaaa8812
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestSplineFunctions.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.test.library.stats;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestSplineFunctions extends TestBase {
+
+    @Test
+    public void basicSplineCoef() {
+        // method "periodic"
+        assertEval(".Call(stats:::C_SplineCoef, 1, c(1), c(1))");
+        assertEval(".Call(stats:::C_SplineCoef, 1, c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, 1, c(1:6), c(1:5, 1))");
+        // method "natural"
+        assertEval(".Call(stats:::C_SplineCoef, 2, c(1), c(1))");
+        assertEval(".Call(stats:::C_SplineCoef, 2, c(1:5), c(1:5))");
+        // method "fmm"
+        assertEval(".Call(stats:::C_SplineCoef, 3, c(1:5), c(1:5))");
+        // method "hyman"
+        assertEval(".Call(stats:::C_SplineCoef, 4, c(1:5), c(1:5))");
+
+        assertEval(".Call(stats:::C_SplineCoef, 0, c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, -1, c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, 111, c(1:5), c(1:5))");
+
+        assertEval(".Call(stats:::C_SplineCoef, NULL, c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, NA, c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, c(), c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, list(), c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, c(list()), c(1:5), c(1:5))");
+        assertEval(Ignored.WrongCaller, ".Call(stats:::C_SplineCoef, 'abc', c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, c(1), c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, c(1, 2, 3), c(1), c(1))");
+        assertEval(Ignored.WrongCaller, ".Call(stats:::C_SplineCoef, c('a'), c(1), c(1))");
+        assertEval(Ignored.WrongCaller, ".Call(stats:::C_SplineCoef, list(1), c(1), c(1))");
+        assertEval(Ignored.WrongCaller, ".Call(stats:::C_SplineCoef, environment(), c(1:5), c(1:5))");
+        assertEval(Ignored.WrongCaller, ".Call(stats:::C_SplineCoef, function() {}, c(1:5), c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, 1, list(1), c(1))");
+        assertEval(".Call(stats:::C_SplineCoef, 1, c(1), list(1))");
+
+        assertEval(".Call(stats:::C_SplineCoef, 1, NULL, c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, 1, NA, c(1:5))");
+        assertEval(".Call(stats:::C_SplineCoef, 1, c(1:5), NULL)");
+        assertEval(".Call(stats:::C_SplineCoef, 1, c(1:5), NA)");
+        assertEval(".Call(stats:::C_SplineCoef, 1, NULL, NULL)");
+        assertEval(".Call(stats:::C_SplineCoef, 1, NA, NA)");
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTypeConvert.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTypeConvert.java
index 972ac0da24..2096f7059b 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTypeConvert.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTypeConvert.java
@@ -58,4 +58,41 @@ public class TestTypeConvert extends TestBase {
         // UnsupportedSpecializationException: Unexpected values provided for ...
         assertEval(Ignored.Unimplemented, "type.convert('NA', 1)");
     }
+
+    @Test
+    public void typeConvertExternal2Test() {
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', NA, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', NULL, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', 'TRUE', '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', 'abc', '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', list('abc'), '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', list(), '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', c(), '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', c(NULL), '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', 1, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', 2, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', environment(), '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), 'NA', function() {}, '.', 'allow.loss')");
+
+        assertEval(".External2(utils:::C_typeconvert, NULL, 'NA', FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, NA, 'NA', FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(), 'NA', FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), NULL, FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), NA, FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), c(), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(1), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(1, TRUE), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(1, 'TRUE'), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(1, 'TRUE', 'x'), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(1, '1'), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c(1, 'x'), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), c(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, list(1), list(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, list('1'), list('1'), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, list('1'), list(1), FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, environment(), 'NA', FALSE, '.', 'allow.loss')");
+        assertEval(".External2(utils:::C_typeconvert, c('1'), environment(), FALSE, '.', 'allow.loss')");
+    }
+
 }
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index e215042965..5e9c6ebfd3 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -407,7 +407,7 @@ def _test_subpackage(name):
     return '.'.join((_test_package(), name))
 
 def _simple_generated_unit_tests():
-    return ','.join(map(_test_subpackage, ['library.base', 'library.grid', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'parser', 'S4', 'rng', 'runtime.data']))
+    return ','.join(map(_test_subpackage, ['library.base', 'library.grid', 'library.methods', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'parser', 'S4', 'rng', 'runtime.data']))
 
 def _simple_unit_tests():
     return ','.join([_simple_generated_unit_tests(), _test_subpackage('tck')])
-- 
GitLab