From ebd3ae65c1eb406e56eb08f69a99c799715cfec3 Mon Sep 17 00:00:00 2001
From: Zbynek Slajchrt <zbynek.slajchrt@oracle.com>
Date: Thu, 19 Jan 2017 14:24:24 +0100
Subject: [PATCH] Cast pipelines added to another batch of ext builtins

---
 .../library/methods/MethodsListDispatch.java  | 26 +++++---
 .../truffle/r/library/methods/Slot.java       | 59 ++++++++++---------
 .../truffle/r/library/tools/DirChmod.java     | 23 ++++----
 .../truffle/r/library/tools/ToolsText.java    | 33 +++++++++--
 .../truffle/r/nodes/builtin/base/CumProd.java | 22 -------
 .../r/nodes/builtin/base/Quantifier.java      |  2 +-
 .../com/oracle/truffle/r/runtime/RError.java  |  3 +-
 .../truffle/r/runtime/data/RS4Object.java     |  6 +-
 .../truffle/r/test/ExpectedTestOutput.test    | 48 +++++++++++++++
 .../TestExternal_R_getClassFromCache.java     | 37 ++++++++++++
 .../library/stats/TestExternal_R_getSlot.java | 37 ++++++++++++
 .../stats/TestExternal_doTabExpand.java       | 40 +++++++++++++
 12 files changed, 258 insertions(+), 78 deletions(-)
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getClassFromCache.java
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getSlot.java
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_doTabExpand.java

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 08761a46f9..0bcfa1aecf 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,6 +11,9 @@
  */
 package com.oracle.truffle.r.library.methods;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
@@ -48,7 +51,6 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -57,8 +59,6 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
-
 // Transcribed (unless otherwise noted) from src/library/methods/methods_list_dispatch.c
 
 public class MethodsListDispatch {
@@ -101,6 +101,17 @@ public class MethodsListDispatch {
 
     public abstract static class R_getClassFromCache extends RExternalBuiltinNode.Arg2 {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            //@formatter:off
+            casts.arg(0, "klass").defaultError(RError.Message.GENERIC, "class should be either a character-string name or a class definition").
+                mustNotBeNull().
+                mustBe(stringValue().or(instanceOf(RS4Object.class)));
+
+            casts.arg(1, "table").mustNotBeNull(RError.NO_CALLER, RError.Message.USE_NULL_ENV_DEFUNCT).mustBe(instanceOf(REnvironment.class));
+            //@formatter:on
+        }
+
         protected GetFixedAttributeNode createPckgAttrAccess() {
             return GetFixedAttributeNode.create(RRuntime.PCKG_ATTR_KEY);
         }
@@ -112,6 +123,10 @@ public class MethodsListDispatch {
                         @Cached("createPckgAttrAccess()") GetFixedAttributeNode valPckgAttrAccess) {
             String klassString = klass.getLength() == 0 ? RRuntime.STRING_NA : klass.getDataAt(0);
 
+            if (klassString.length() == 0) {
+                throw RError.error(RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
+            }
+
             Object value = table.get(klassString);
             if (value == null) {
                 return RNull.instance;
@@ -136,11 +151,6 @@ public class MethodsListDispatch {
             return klass;
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object callGetClassFromCache(Object klass, Object table) {
-            throw RError.error(this, RError.Message.GENERIC, "class should be either a character-string name or a class definition");
-        }
     }
 
     public abstract static class R_set_method_dispatch extends RExternalBuiltinNode.Arg1 {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/Slot.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/Slot.java
index 03eb4418b5..592065c8b9 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/Slot.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/Slot.java
@@ -5,25 +5,28 @@
  *
  * Copyright (c) 1995-2013, The R Core Team
  * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
 package com.oracle.truffle.r.library.methods;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+
 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.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNode;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastToAttributableNode;
 import com.oracle.truffle.r.nodes.unary.CastToAttributableNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 // Transcribed from src/library/methods/src/slot.c
 
@@ -34,25 +37,26 @@ public class Slot {
         @Child private AccessSlotNode accessSlotNode = AccessSlotNodeGen.create(false, null, null);
         @Child private CastToAttributableNode castAttributable = CastToAttributableNodeGen.create(true, true, true);
 
-        protected static String getInternedName(RAbstractStringVector nameVec) {
-            return Utils.intern(nameVec.getDataAt(0));
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(1, "name").defaultError(RError.NO_CALLER, RError.Message.GENERIC, "invalid type or length for slot name").mustNotBeNull().mustBe(stringValue()).asStringVector().mustBe(
+                            singleElement()).findFirst().mustBe(Predef.lengthGt(0), RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
         }
 
-        @Specialization(guards = {"nameVec.getLength() == 1", "nameVec.getDataAt(0).equals(cachedInternedName)"})
-        protected Object getSlotCached(Object object, @SuppressWarnings("unused") RAbstractStringVector nameVec, @Cached("getInternedName(nameVec)") String cachedInternedName) {
-            return accessSlotNode.executeAccess(castAttributable.executeObject(object), cachedInternedName);
+        protected static String getInternedName(String name) {
+            return Utils.intern(name);
         }
 
-        @Specialization(contains = "getSlotCached", guards = "nameVec.getLength() == 1")
-        protected Object getSlot(Object object, RAbstractStringVector nameVec) {
-            return accessSlotNode.executeAccess(castAttributable.executeObject(object), getInternedName(nameVec));
+        @Specialization(guards = {"name.equals(cachedInternedName)"})
+        protected Object getSlotCached(Object object, @SuppressWarnings("unused") String name, @Cached("getInternedName(name)") String cachedInternedName) {
+            return accessSlotNode.executeAccess(castAttributable.executeObject(object), cachedInternedName);
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object getSlot(Object object, Object nameVec) {
-            throw RError.error(this, RError.Message.GENERIC, "invalid type or length for slot name");
+        @Specialization(contains = "getSlotCached")
+        protected Object getSlot(Object object, String name) {
+            return accessSlotNode.executeAccess(castAttributable.executeObject(object), getInternedName(name));
         }
+
     }
 
     public abstract static class R_setSlot extends RExternalBuiltinNode.Arg3 {
@@ -60,24 +64,25 @@ public class Slot {
         @Child private UpdateSlotNode updateSlotNode = UpdateSlotNodeGen.create(null, null, null);
         @Child private CastToAttributableNode castAttributable = CastToAttributableNodeGen.create(true, true, true);
 
-        protected static String getInternedName(RAbstractStringVector nameVec) {
-            return Utils.intern(nameVec.getDataAt(0));
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(1, "name").defaultError(RError.NO_CALLER, RError.Message.GENERIC, "invalid type or length for slot name").mustNotBeNull().mustBe(stringValue()).asStringVector().mustBe(
+                            singleElement()).findFirst().mustBe(Predef.lengthGt(0), RError.NO_CALLER, RError.Message.ZERO_LENGTH_VARIABLE);
         }
 
-        @Specialization(guards = {"nameVec.getLength() == 1", "nameVec.getDataAt(0).equals(cachedInternedName)"})
-        protected Object setSlotCached(Object object, @SuppressWarnings("unused") RAbstractStringVector nameVec, Object value, @Cached("getInternedName(nameVec)") String cachedInternedName) {
-            return updateSlotNode.executeUpdate(castAttributable.executeObject(object), cachedInternedName, value);
+        protected static String getInternedName(String name) {
+            return Utils.intern(name);
         }
 
-        @Specialization(contains = "setSlotCached", guards = "nameVec.getLength() == 1")
-        protected Object setSlot(Object object, RAbstractStringVector nameVec, Object value) {
-            return updateSlotNode.executeUpdate(castAttributable.executeObject(object), getInternedName(nameVec), value);
+        @Specialization(guards = {"name.equals(cachedInternedName)"})
+        protected Object setSlotCached(Object object, @SuppressWarnings("unused") String name, Object value, @Cached("getInternedName(name)") String cachedInternedName) {
+            return updateSlotNode.executeUpdate(castAttributable.executeObject(object), cachedInternedName, value);
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected Object setSlot(Object object, Object name, Object value) {
-            throw RError.error(this, RError.Message.GENERIC, "invalid type or length for slot name");
+        @Specialization(contains = "setSlotCached")
+        protected Object setSlot(Object object, String name, Object value) {
+            return updateSlotNode.executeUpdate(castAttributable.executeObject(object), getInternedName(name), value);
         }
+
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
index 922d849228..c86c881af6 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
@@ -5,12 +5,16 @@
  *
  * Copyright (c) 1995-2015, The R Core Team
  * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
 package com.oracle.truffle.r.library.tools;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+
 import java.io.IOException;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
@@ -22,13 +26,12 @@ import java.util.stream.Stream;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 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.Utils;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class DirChmod extends RExternalBuiltinNode.Arg2 {
@@ -38,15 +41,15 @@ public abstract class DirChmod extends RExternalBuiltinNode.Arg2 {
     private static final int FILE_MASK = 0644;
     private static final int DIR_MASK = 0755;
 
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg(0, "dir").mustNotBeNull().mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+        casts.arg(1).asLogicalVector().findFirst(RRuntime.LOGICAL_NA).map(toBoolean());
+    }
+
     @Specialization
     @TruffleBoundary
-    protected RNull dirChmod(RAbstractStringVector dir, Object gws) {
-        if (dir.getLength() != 1) {
-            throw RError.error(this, RError.Message.INVALID_ARGUMENT, "dir");
-        }
-        String pathName = ((RStringVector) dir).getDataAt(0);
-        boolean setGroupWrite = RRuntime.fromLogical(castLogical(castVector(gws)));
-
+    protected RNull dirChmod(String pathName, boolean setGroupWrite) {
         Path path = FileSystems.getDefault().getPath(pathName);
         int fileMask = setGroupWrite ? GRPWRITE_FILE_MASK : FILE_MASK;
         int dirMask = setGroupWrite ? GRPWRITE_DIR_MASK : DIR_MASK;
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 07b4e918d8..9162e0ac2c 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
@@ -11,15 +11,23 @@
  */
 package com.oracle.truffle.r.library.tools;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+
 import java.io.BufferedOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.function.Function;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
+import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -31,6 +39,17 @@ public class ToolsText {
 
     public abstract static class DoTabExpand extends RExternalBuiltinNode.Arg2 {
 
+        @Child private TypeofNode typeofNode = com.oracle.truffle.r.nodes.unary.TypeofNodeGen.create();
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            Function<Object, String> argTypeName = arg -> typeofNode.execute(arg).getName();
+
+            casts.arg(0, "strings").defaultError(RError.NO_CALLER, RError.Message.MACRO_CAN_BE_APPLIED_TO, "STRING_ELT()", "character vector", argTypeName).mustNotBeNull().mustBe(stringValue());
+            casts.arg(1, "starts").defaultError(RError.NO_CALLER, RError.Message.MACRO_CAN_BE_APPLIED_TO, "INTEGER()", "integer", argTypeName).mustNotBeNull().mustBe(
+                            Predef.integerValue()).asIntegerVector();
+        }
+
         @Specialization
         @TruffleBoundary
         protected Object doTabExpand(RAbstractStringVector strings, RAbstractIntVector starts) {
@@ -40,7 +59,7 @@ public class ToolsText {
                 if (input.indexOf('\t') >= 0) {
                     StringBuffer sb = new StringBuffer();
                     int b = 0;
-                    int start = starts.getDataAt(i % data.length);
+                    int start = starts.getDataAt(i % starts.getLength());
                     for (int sx = 0; sx < input.length(); sx++) {
                         char ch = input.charAt(sx);
                         if (ch == '\n') {
@@ -67,16 +86,18 @@ public class ToolsText {
 
     public abstract static class CodeFilesAppend extends RExternalBuiltinNode.Arg2 {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(0, "file1").mustNotBeNull().mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+            casts.arg(1, "file2").mustNotBeNull().mustBe(stringValue()).asStringVector();
+        }
+
         @Specialization
         @TruffleBoundary
-        protected Object codeFilesAppend(RAbstractStringVector file1Vector, RAbstractStringVector file2) {
-            if (file1Vector.getLength() != 1) {
-                throw RError.error(this, RError.Message.INVALID_ARGUMENT, "file1");
-            }
+        protected Object codeFilesAppend(String file1, RAbstractStringVector file2) {
             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)) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
index bd45200b6c..352ea43b6c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
@@ -75,28 +75,6 @@ public abstract class CumProd extends RBuiltinNode {
         return RDataFactory.createDoubleVector(new double[0], true, emptyVec.getNames());
     }
 
-    @Specialization
-    protected RIntVector cumprod(RAbstractIntVector arg) {
-        int[] array = new int[arg.getLength()];
-        na.enable(arg);
-        int prev = 1;
-        int i;
-        for (i = 0; i < arg.getLength(); i++) {
-            if (na.check(arg.getDataAt(i))) {
-                break;
-            }
-            prev = mul.op(prev, arg.getDataAt(i));
-            if (na.check(prev)) {
-                break;
-            }
-            array[i] = prev;
-        }
-        if (!na.neverSeenNA()) {
-            Arrays.fill(array, i, array.length, RRuntime.INT_NA);
-        }
-        return RDataFactory.createIntVector(array, !na.neverSeenNA(), getNamesNode.getNames(arg));
-    }
-
     @Specialization
     protected RDoubleVector cumprod(RAbstractDoubleVector arg) {
         double[] array = new double[arg.getLength()];
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java
index 6836747194..1b88a32c14 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quantifier.java
@@ -58,7 +58,6 @@ public abstract class Quantifier extends RBuiltinNode {
     private final BranchProfile falseBranch = BranchProfile.create();
 
     @Child private TypeofNode typeofNode = com.oracle.truffle.r.nodes.unary.TypeofNodeGen.create();
-    private final Function<Object, String> argTypeName = arg -> typeofNode.execute(arg).getName();
 
     @Children private final CastNode[] argCastNodes = new CastNode[MAX_CACHED_LENGTH];
 
@@ -89,6 +88,7 @@ public abstract class Quantifier extends RBuiltinNode {
 
     private void createArgCast(int index) {
         CastBuilder argCastBuilder = new CastBuilder();
+        Function<Object, String> argTypeName = arg -> typeofNode.execute(arg).getName();
         argCastBuilder.arg(0).allowNull().shouldBe(integerValue().or(logicalValue()).or(instanceOf(RAbstractVector.class).and(size(0))), RError.Message.COERCING_ARGUMENT, argTypeName,
                         "logical").asLogicalVector();
         argCastNodes[index] = insert(new ProfileCastNode(argCastBuilder.getCasts()[0]));
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 adedfbb1e5..51a817d01c 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
@@ -779,7 +779,8 @@ public final class RError extends RuntimeException {
         MORE_THAN_ONE_MATCH("there is more than one match in '%s'"),
         TOO_MANY_ARGS("too many arguments"),
         ARG_MUST_BE_CHARACTER("argument '%s' must be character"),
-        INCORRECT_NOF_ARGS("Incorrect number of arguments (%d), expecting %d for '%s'");
+        INCORRECT_NOF_ARGS("Incorrect number of arguments (%d), expecting %d for '%s'"),
+        MACRO_CAN_BE_APPLIED_TO("%s can only be applied to a '%s', not a '%s'");
 
         public final String message;
         final boolean hasArgs;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RS4Object.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RS4Object.java
index 4c10a99ed8..9fca460896 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RS4Object.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RS4Object.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -28,7 +28,7 @@ import com.oracle.truffle.r.runtime.RType;
  * This is a placeholder class for an S4 object (GnuR S4SXP). It has no functionality at present but
  * is needed as such objects are generated when unserializing the "methods" package.
  */
-public class RS4Object extends RSharingAttributeStorage {
+public final class RS4Object extends RSharingAttributeStorage {
 
     private static final RStringVector implicitClass = RDataFactory.createStringVectorFromScalar("S4");
 
@@ -37,7 +37,7 @@ public class RS4Object extends RSharingAttributeStorage {
     }
 
     @Override
-    public final RStringVector getImplicitClass() {
+    public RStringVector getImplicitClass() {
         return implicitClass;
     }
 
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index c908bb2547..105ccc56b2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -130292,6 +130292,30 @@ Error: invalid 'n' argument
 #.Call(stats:::C_BinDist, c(1,2,3), c(4,5,6), 0, 3, c(NA, 3L))
 Error: invalid 'n' argument
 
+##com.oracle.truffle.r.test.library.stats.TestExternal_R_getClassFromCache.testGetClassFromCache#
+#.Call(methods:::C_R_getClassFromCache, "", new.env())
+Error: attempt to use zero-length variable name
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_R_getClassFromCache.testGetClassFromCache#
+#.Call(methods:::C_R_getClassFromCache, "aaa", NULL)
+Error: use of NULL environment is defunct
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_R_getClassFromCache.testGetClassFromCache#
+#.Call(methods:::C_R_getClassFromCache, "aaa", new.env())
+NULL
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_R_getSlot.testGetPrimName#
+#.Call(methods:::C_R_get_slot,"","")
+Error: attempt to use zero-length variable name
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_R_getSlot.testGetPrimName#
+#.Call(methods:::C_R_get_slot,"",NULL)
+Error: invalid type or length for slot name
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_R_getSlot.testGetPrimName#
+#.Call(methods:::C_R_get_slot,"",c("a","b"))
+Error: invalid type or length for slot name
+
 ##com.oracle.truffle.r.test.library.stats.TestExternal_R_get_prim_name.testGetPrimName#
 #.Call(methods:::C_R_get_primname, "abc")
 Error: 'R_get_primname' called on a non-primitive
@@ -130360,6 +130384,30 @@ Error: 'x' is NULL
 #.Call(stats:::C_cov, c('1','2','3','4','5'), 1:5, 4, FALSE)
 [1] 2.5
 
+##com.oracle.truffle.r.test.library.stats.TestExternal_doTabExpand.testDoTabExpand#
+#.Call(tools:::doTabExpand,"a	b",1.1)
+Error: INTEGER() can only be applied to a 'integer', not a 'double'
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_doTabExpand.testDoTabExpand#
+#.Call(tools:::doTabExpand,"a	b",1L)
+[1] "a      b"
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_doTabExpand.testDoTabExpand#
+#.Call(tools:::doTabExpand,"a	b",NULL)
+Error: INTEGER() can only be applied to a 'integer', not a 'NULL'
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_doTabExpand.testDoTabExpand#
+#.Call(tools:::doTabExpand,.Call(tools:::doTabExpand,c(1,2),c(0L,1L)),1L)
+Error: STRING_ELT() can only be applied to a 'character vector', not a 'double'
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_doTabExpand.testDoTabExpand#
+#.Call(tools:::doTabExpand,123,1L)
+Error: STRING_ELT() can only be applied to a 'character vector', not a 'double'
+
+##com.oracle.truffle.r.test.library.stats.TestExternal_doTabExpand.testDoTabExpand#
+#.Call(tools:::doTabExpand,c("a	b	c","x	y	z"),c(0L,1L))
+[1] "a       b       c" "x      y       z"
+
 ##com.oracle.truffle.r.test.library.stats.TestExternal_qgamma.testQgamma#
 #qgamma(0, 1)
 [1] 0
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getClassFromCache.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getClassFromCache.java
new file mode 100644
index 0000000000..e215342eb7
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getClassFromCache.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.test.library.stats;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestExternal_R_getClassFromCache extends TestBase {
+    @Test
+    public void testGetClassFromCache() {
+        assertEval(".Call(methods:::C_R_getClassFromCache, \"aaa\", new.env())");
+        assertEval(".Call(methods:::C_R_getClassFromCache, \"aaa\", NULL)");
+        assertEval(".Call(methods:::C_R_getClassFromCache, \"\", new.env())");
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getSlot.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getSlot.java
new file mode 100644
index 0000000000..90089e8989
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_R_getSlot.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.test.library.stats;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestExternal_R_getSlot extends TestBase {
+    @Test
+    public void testGetPrimName() {
+        assertEval(".Call(methods:::C_R_get_slot,\"\",\"\")");
+        assertEval(".Call(methods:::C_R_get_slot,\"\",c(\"a\",\"b\"))");
+        assertEval(".Call(methods:::C_R_get_slot,\"\",NULL)");
+    }
+}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_doTabExpand.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_doTabExpand.java
new file mode 100644
index 0000000000..c060ffa19d
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_doTabExpand.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.test.library.stats;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestExternal_doTabExpand extends TestBase {
+    @Test
+    public void testDoTabExpand() {
+        assertEval(".Call(tools:::doTabExpand,\"a\tb\",1L)");
+        assertEval(".Call(tools:::doTabExpand,c(\"a\tb\tc\",\"x\ty\tz\"),c(0L,1L))");
+        assertEval(".Call(tools:::doTabExpand,\"a\tb\",NULL)");
+        assertEval(".Call(tools:::doTabExpand,\"a\tb\",1.1)");
+        assertEval(".Call(tools:::doTabExpand,123,1L)");
+        assertEval(".Call(tools:::doTabExpand,.Call(tools:::doTabExpand,c(1,2),c(0L,1L)),1L)");
+    }
+}
-- 
GitLab