diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index 80b2d4eb141463eb588c35467f57271848a8d583..eadcc67c1691925fac1754d3f54317dbf876dcd6 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -48,6 +48,7 @@ import com.oracle.truffle.api.impl.FindContextNode;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
@@ -102,6 +103,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
@@ -384,7 +386,7 @@ final class REngine implements Engine, Engine.Timings {
         for (int i = 0; i < exprs.getLength(); i++) {
             Object obj = RASTUtils.checkForRSymbol(exprs.getDataAt(i));
             if (obj instanceof RLanguage) {
-                result = evalNode((RNode) ((RLanguage) obj).getRep(), envir, depth);
+                result = evalNode(((RLanguage) obj).getRep().asRSyntaxNode(), envir, depth);
             } else {
                 result = obj;
             }
@@ -394,7 +396,7 @@ final class REngine implements Engine, Engine.Timings {
 
     @Override
     public Object eval(RLanguage expr, REnvironment envir, int depth) {
-        return evalNode((RNode) expr.getRep(), envir, depth);
+        return evalNode(expr.getRep().asRSyntaxNode(), envir, depth);
     }
 
     @Override
@@ -432,11 +434,11 @@ final class REngine implements Engine, Engine.Timings {
         return func.getTarget().call(rArgs);
     }
 
-    private Object evalNode(RNode exprRep, REnvironment envir, int depth) {
+    private Object evalNode(RSyntaxElement exprRep, REnvironment envir, int depth) {
         // we need to copy the node, otherwise it (and its children) will specialized to a specific
         // frame descriptor and will fail on subsequent re-executions
-        RNode n = (RNode) exprRep.deepCopy();
-        RootCallTarget callTarget = doMakeCallTarget(n, EVAL_FUNCTION_NAME, false, false);
+        RSyntaxNode n = RContext.getASTBuilder().process(exprRep);
+        RootCallTarget callTarget = doMakeCallTarget(n.asRNode(), EVAL_FUNCTION_NAME, false, false);
         RCaller call = RArguments.getCall(envir.getFrame());
         return evalTarget(callTarget, call, envir, depth);
     }
@@ -484,6 +486,7 @@ final class REngine implements Engine, Engine.Timings {
     private final class AnonymousRootNode extends RootNode {
 
         private final ValueProfile frameTypeProfile = ValueProfile.createClassProfile();
+        private final ConditionProfile isVirtualFrameProfile = ConditionProfile.createBinaryProfile();
 
         private final String description;
         private final boolean printResult;
@@ -499,10 +502,21 @@ final class REngine implements Engine, Engine.Timings {
             this.topLevel = topLevel;
         }
 
+        private VirtualFrame prepareFrame(VirtualFrame frame) {
+            VirtualFrame vf;
+            MaterializedFrame originalFrame = (MaterializedFrame) frameTypeProfile.profile(frame.getArguments()[0]);
+            if (isVirtualFrameProfile.profile(originalFrame instanceof VirtualFrame)) {
+                vf = (VirtualFrame) originalFrame;
+            } else {
+                vf = SubstituteVirtualFrame.create(originalFrame);
+            }
+            return vf;
+        }
+
         @Override
         public Object execute(VirtualFrame frame) {
             assert frame.getArguments().length == 1;
-            VirtualFrame vf = SubstituteVirtualFrame.create((MaterializedFrame) frameTypeProfile.profile(frame.getArguments()[0]));
+            VirtualFrame vf = prepareFrame(frame);
             Object result = null;
             try {
                 result = body.execute(vf);
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 590fdc5b8f241f2ab63c3b14aabe3d21965309d5..3387c39a9edb20d3fe757c41f8760495f60b4932 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -48,7 +48,6 @@ import com.oracle.truffle.r.nodes.control.IfNode;
 import com.oracle.truffle.r.nodes.control.ReplacementNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
-import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.Arguments;
@@ -268,16 +267,14 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
 
     private static RNode unwrapToRNode(Object objArg) {
         Object obj = objArg;
+        // obj is RSymbol or a primitive value.
+        // A symbol needs to be converted back to a ReadVariableNode
         if (obj instanceof RLanguage) {
             return (RNode) RASTUtils.unwrap(((RLanguage) obj).getRep());
+        } else if (obj instanceof RSymbol) {
+            return ReadVariableNode.create(((RSymbol) obj).getName());
         } else {
-            // obj is RSymbol or a primitive value.
-            // A symbol needs to be converted back to a ReadVariableNode
-            if (obj instanceof RSymbol) {
-                return ReadVariableNode.create(((RSymbol) obj).getName());
-            } else {
-                return ConstantNode.create(obj);
-            }
+            return ConstantNode.create(obj);
         }
     }
 
@@ -299,7 +296,8 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
     @TruffleBoundary
     public RStringVector getNames(RLanguage rl) {
         RBaseNode node = rl.getRep();
-        if (node instanceof RCallNode || node instanceof GroupDispatchNode) {
+        if (node instanceof RCallNode) {
+            RCallNode call = (RCallNode) node;
             /*
              * If the function or any argument has a name, then all arguments (and the function) are
              * given names, with unnamed arguments getting "". However, if no arguments have names,
@@ -307,13 +305,12 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
              */
             boolean hasName = false;
             String functionName = "";
-            RNode fnNode = RASTUtils.getFunctionNode(node);
+            RNode fnNode = call.getFunctionNode();
             if (fnNode instanceof NamedRNode) {
                 hasName = true;
                 functionName = ((NamedRNode) fnNode).name;
             }
-            Arguments<RSyntaxNode> args = RASTUtils.findCallArguments(node);
-            ArgumentsSignature sig = args.getSignature();
+            ArgumentsSignature sig = call.getSyntaxSignature();
             if (!hasName) {
                 for (int i = 0; i < sig.getLength(); i++) {
                     if (sig.getName(i) != null) {
@@ -342,7 +339,8 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
     public void setNames(RLanguage rl, RStringVector names) {
         RNode node = (RNode) rl.getRep();
         if (node instanceof RCallNode) {
-            Arguments<RSyntaxNode> args = RASTUtils.findCallArguments(node);
+            RCallNode call = (RCallNode) node;
+            Arguments<RSyntaxNode> args = call.getArguments();
             ArgumentsSignature sig = args.getSignature();
             String[] newNames = new String[sig.getLength()];
             int argNamesLength = names.getLength() - 1;
@@ -354,8 +352,6 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
             }
             // copying is already handled by RShareable
             rl.setRep(RCallNode.createCall(RSyntaxNode.INTERNAL, ((RCallNode) node).getFunctionNode(), ArgumentsSignature.get(newNames), args.getArguments()));
-        } else if (node instanceof GroupDispatchNode) {
-            throw RError.nyi(null, "group dispatch names update");
         } else {
             throw RInternalError.shouldNotReachHere();
         }
@@ -601,7 +597,7 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         String className = tag.getSimpleName();
         switch (className) {
             case "CallTag":
-                return node instanceof RCallNode || node instanceof GroupDispatchNode;
+                return node instanceof RCallNode;
 
             case "StatementTag": {
                 Node parent = ((RInstrumentableNode) node).unwrapParent();
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 8b323a7fdcc6a4628f197ac642032030265de0ec..682b91fa607cef84a868fd069418f2b0e9bef573 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
@@ -379,7 +379,7 @@ public class MethodsListDispatch {
             }
             RCallNode callNode = (RCallNode) matchedCall.getRep();
             RNode f = ReadVariableNode.create(RRuntime.R_DOT_NEXT_METHOD);
-            ArgumentsSignature sig = callNode.getArguments().getSignature();
+            ArgumentsSignature sig = callNode.getSyntaxSignature();
             RSyntaxNode[] args = new RSyntaxNode[sig.getLength()];
             for (int i = 0; i < args.length; i++) {
                 args[i] = ReadVariableNode.create(sig.getName(i));
diff --git a/com.oracle.truffle.r.native/library/parallel/RSHAREDnode.R b/com.oracle.truffle.r.native/library/parallel/RSHAREDnode.R
index ed11ae43b6c4918679b88f71ca87d006f6f0886f..fb0c28ded5ce556c0d1ca55126646d358b36028e 100644
--- a/com.oracle.truffle.r.native/library/parallel/RSHAREDnode.R
+++ b/com.oracle.truffle.r.native/library/parallel/RSHAREDnode.R
@@ -1,5 +1,5 @@
 makeSHAREDmaster <- function(key) {
-    channel <- fastr:::fastr.channel.get(as.integer(key))
+    channel <- .fastr.channel.get(as.integer(key))
     structure(list(channel=channel), class = "SHAREDnode")
 }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java
index 38789f6c31b05e8622637f3bbde62bb4dfd9fa29..f7be50677a4077bef69c530505b797df8c0d4892 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java
@@ -33,17 +33,10 @@ import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 @RBuiltin(name = "anyNA", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.INTERNAL_GENERIC)
@@ -140,11 +133,6 @@ public abstract class AnyNA extends RBuiltinNode {
         return doScalar(false);
     }
 
-    @Specialization
-    protected byte isNA(RFactor value) {
-        return doVector(value.getVector(), (v, i) -> naCheck.check(v.getDataAt(i)));
-    }
-
     protected AnyNA createRecursive() {
         return AnyNANodeGen.create(null);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java
index fa8dec148fd08e6e98b32a2e18097b45c6269008..5b8d1e5d4807c0f7d0af5120f518d7946bbbd4de 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java
@@ -30,13 +30,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen;
 import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 @RBuiltin(name = "as.double", aliases = {"as.numeric"}, kind = PRIMITIVE, parameterNames = {"x", "..."})
@@ -116,9 +110,4 @@ public abstract class AsDouble extends RBuiltinNode {
         initCast();
         return (RDoubleVector) castDoubleNode.executeDouble(vector);
     }
-
-    @Specialization
-    protected RDoubleVector asDouble(RFactor vector) {
-        return asDouble(vector.getVector());
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
index 5f760658c2501b741868d7cbef536235cd53c8ea..02ea4ef335d976d9c416d0d91fc283a6866b1990 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
@@ -31,13 +31,7 @@ import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRaw;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
@@ -119,11 +113,6 @@ public abstract class AsInteger extends RBuiltinNode {
         return (RAbstractIntVector) castIntNode.executeInt(vector);
     }
 
-    @Specialization
-    protected RIntVector asInteger(RFactor factor) {
-        return asInteger(factor.getVector());
-    }
-
     @Specialization
     protected int asInteger(RConnection conn) {
         return conn.getDescriptor();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java
index 7d067318ffd014d0e830cb5de23443bbab1aa67a..b7c3348fa9ac9ac44554ad2aa232bf2802507337 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java
@@ -24,243 +24,233 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 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.api.dsl.TypeSystemReference;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastComplexNode;
-import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
-import com.oracle.truffle.r.nodes.unary.CastExpressionNode;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
-import com.oracle.truffle.r.nodes.unary.CastListNode;
-import com.oracle.truffle.r.nodes.unary.CastListNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
-import com.oracle.truffle.r.nodes.unary.CastRawNode;
-import com.oracle.truffle.r.nodes.unary.CastSymbolNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RInteger;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogical;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.nodes.builtin.base.AsVectorNodeGen.AsVectorInternalNodeGen;
+import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
+import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
+import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
+import com.oracle.truffle.r.nodes.function.UseMethodInternalNode;
+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.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 @RBuiltin(name = "as.vector", kind = INTERNAL, parameterNames = {"x", "mode"})
 public abstract class AsVector extends RBuiltinNode {
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private AsVectorInternal internal = AsVectorInternalNodeGen.create();
+    @Child private ClassHierarchyNode classHierarchy = ClassHierarchyNodeGen.create(false, false);
+    @Child private UseMethodInternalNode useMethod;
+
+    private final ConditionProfile hasClassProfile = ConditionProfile.createBinaryProfile();
 
     @Override
     protected void createCasts(CastBuilder casts) {
         casts.firstStringWithError(1, RError.Message.INVALID_ARGUMENT, "mode");
     }
 
-    @Specialization
-    protected Object asVector(RNull x, @SuppressWarnings("unused") RMissing mode) {
-        controlVisibility();
-        return x;
+    protected static AsVectorInternal createInternal() {
+        return AsVectorInternalNodeGen.create();
     }
 
-    @Specialization(guards = "castToString(mode)")
-    protected Object asVectorString(Object x, @SuppressWarnings("unused") String mode, //
-                    @Cached("create()") AsCharacter asCharacter) {
-        controlVisibility();
-        return asCharacter.execute(x);
-    }
+    private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("x", "mode");
 
-    @Specialization(guards = "castToInt(x, mode)")
-    protected Object asVectorInt(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastIntegerNode cast) {
+    @Specialization
+    protected Object asVector(VirtualFrame frame, Object x, String mode) {
         controlVisibility();
-        return cast.execute(x);
+        RStringVector clazz = classHierarchy.execute(x);
+        if (hasClassProfile.profile(clazz != null)) {
+            if (useMethod == null) {
+                // Note: this dispatch takes care of factor, because there is as.vector.factor
+                // specialization in R
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                useMethod = insert(new UseMethodInternalNode("as.vector", SIGNATURE, false));
+            }
+            try {
+                return useMethod.execute(frame, clazz, new Object[]{x, mode});
+            } catch (S3FunctionLookupNode.NoGenericMethodException e) {
+                // fallthrough
+            }
+        }
+        return internal.execute(x, mode);
     }
 
-    @Specialization(guards = "castToDouble(x, mode)")
-    protected Object asVectorDouble(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastDoubleNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+    @TypeSystemReference(RTypes.class)
+    public abstract static class AsVectorInternal extends Node {
 
-    @Specialization(guards = "castToComplex(x, mode)")
-    protected Object asVectorComplex(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastComplexNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+        public abstract Object execute(Object x, String mode);
 
-    @Specialization(guards = "castToLogical(x, mode)")
-    protected Object asVectorLogical(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastLogicalNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-    @Specialization(guards = "castToRaw(x, mode)")
-    protected Object asVectorRaw(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastRawNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+        @Specialization(guards = "castToString(mode)")
+        protected Object asVectorString(Object x, @SuppressWarnings("unused") String mode, //
+                        @Cached("create()") AsCharacter asCharacter) {
+            return asCharacter.execute(x);
+        }
 
-    protected static CastListNode createListCast() {
-        return CastListNodeGen.create(true, false, false);
-    }
+        @Specialization(guards = "castToInt(x, mode)")
+        protected Object asVectorInt(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastIntegerNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "castToList(mode)")
-    protected Object asVectorList(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createListCast()") CastListNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+        @Specialization(guards = "castToDouble(x, mode)")
+        protected Object asVectorDouble(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastDoubleNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "castToSymbol(x, mode)")
-    protected Object asVectorSymbol(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastSymbolNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+        @Specialization(guards = "castToComplex(x, mode)")
+        protected Object asVectorComplex(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastComplexNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "castToExpression(mode)")
-    protected Object asVectorExpression(Object x, @SuppressWarnings("unused") String mode, //
-                    @Cached("createNonPreserving()") CastExpressionNode cast) {
-        controlVisibility();
-        return cast.execute(x);
-    }
+        @Specialization(guards = "castToLogical(x, mode)")
+        protected Object asVectorLogical(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastLogicalNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "castToList(mode)")
-    protected RAbstractVector asVectorList(@SuppressWarnings("unused") RNull x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        return RDataFactory.createList();
-    }
+        @Specialization(guards = "castToRaw(x, mode)")
+        protected Object asVectorRaw(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastRawNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "isSymbol(x, mode)")
-    protected RSymbol asVectorSymbol(RSymbol x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        String sName = x.getName();
-        return RDataFactory.createSymbol(sName);
-    }
+        protected static CastListNode createListCast() {
+            return CastListNodeGen.create(true, false, false);
+        }
 
-    protected boolean isSymbol(@SuppressWarnings("unused") RSymbol x, String mode) {
-        return RType.Symbol.getName().equals(mode);
-    }
+        @Specialization(guards = "castToList(mode)")
+        protected Object asVectorList(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createListCast()") CastListNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "modeIsAny(mode)")
-    protected RAbstractVector asVector(RList x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        RList result = x.copyWithNewDimensions(null);
-        result.copyNamesFrom(attrProfiles, x);
-        return result;
-    }
+        @Specialization(guards = "castToSymbol(x, mode)")
+        protected Object asVectorSymbol(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastSymbolNode cast) {
+            return cast.execute(x);
+        }
 
-    @Specialization(guards = "modeIsAny(mode)")
-    protected RAbstractVector asVector(RFactor x, @SuppressWarnings("unused") String mode) {
-        RVector levels = x.getLevels(attrProfiles);
-        RVector result = levels.createEmptySameType(x.getLength(), RDataFactory.COMPLETE_VECTOR);
-        RIntVector factorData = x.getVector();
-        for (int i = 0; i < result.getLength(); i++) {
-            result.transferElementSameType(i, levels, factorData.getDataAt(i) - 1);
+        @Specialization(guards = "castToExpression(mode)")
+        protected Object asVectorExpression(Object x, @SuppressWarnings("unused") String mode, //
+                        @Cached("createNonPreserving()") CastExpressionNode cast) {
+            return cast.execute(x);
         }
-        return result;
-    }
 
-    @Specialization(guards = "modeIsAny(mode)")
-    protected RNull asVector(RNull x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        return x;
-    }
+        @Specialization(guards = "castToList(mode)")
+        protected RAbstractVector asVectorList(@SuppressWarnings("unused") RNull x, @SuppressWarnings("unused") String mode) {
+            return RDataFactory.createList();
+        }
 
-    @Specialization(guards = "modeIsPairList(mode)")
-    protected Object asVectorPairList(RList x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        // TODO implement non-empty element list conversion; this is a placeholder for type test
-        if (x.getLength() == 0) {
-            return RNull.instance;
-        } else {
-            throw RError.nyi(this, "non-empty lists");
+        @Specialization(guards = "isSymbol(x, mode)")
+        protected RSymbol asVectorSymbol(RSymbol x, @SuppressWarnings("unused") String mode) {
+            String sName = x.getName();
+            return RDataFactory.createSymbol(sName);
         }
-    }
 
-    @Specialization(guards = "modeIsAny(mode)")
-    protected RAbstractVector asVectorAny(RAbstractVector x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        return x.copyWithNewDimensions(null);
-    }
+        protected boolean isSymbol(@SuppressWarnings("unused") RSymbol x, String mode) {
+            return RType.Symbol.getName().equals(mode);
+        }
 
-    @Specialization(guards = "modeMatches(x, mode)")
-    protected RAbstractVector asVector(RAbstractVector x, @SuppressWarnings("unused") String mode) {
-        controlVisibility();
-        return x.copyWithNewDimensions(null);
-    }
+        @Specialization(guards = "modeIsAny(mode)")
+        protected RAbstractVector asVector(RList x, @SuppressWarnings("unused") String mode) {
+            RList result = x.copyWithNewDimensions(null);
+            result.copyNamesFrom(attrProfiles, x);
+            return result;
+        }
 
-    protected boolean castToInt(RAbstractContainer x, String mode) {
-        return x.getElementClass() != RInteger.class && RType.Integer.getName().equals(mode);
-    }
+        @Specialization(guards = "modeIsAny(mode)")
+        protected RNull asVector(RNull x, @SuppressWarnings("unused") String mode) {
+            return x;
+        }
 
-    protected boolean castToDouble(RAbstractContainer x, String mode) {
-        return x.getElementClass() != RDouble.class && (RType.Double.getClazz().equals(mode) || RType.Double.getName().equals(mode));
-    }
+        @Specialization(guards = "modeIsPairList(mode)")
+        protected Object asVectorPairList(RList x, @SuppressWarnings("unused") String mode) {
+            // TODO implement non-empty element list conversion; this is a placeholder for type test
+            if (x.getLength() == 0) {
+                return RNull.instance;
+            } else {
+                throw RError.nyi(RError.SHOW_CALLER, "non-empty lists");
+            }
+        }
 
-    protected boolean castToComplex(RAbstractContainer x, String mode) {
-        return x.getElementClass() != RComplex.class && RType.Complex.getName().equals(mode);
-    }
+        @Specialization(guards = "modeIsAny(mode)")
+        protected RAbstractVector asVectorAny(RAbstractVector x, @SuppressWarnings("unused") String mode) {
+            return x.copyWithNewDimensions(null);
+        }
 
-    protected boolean castToLogical(RAbstractContainer x, String mode) {
-        return x.getElementClass() != RLogical.class && RType.Logical.getName().equals(mode);
-    }
+        @Specialization(guards = "modeMatches(x, mode)")
+        protected RAbstractVector asVector(RAbstractVector x, @SuppressWarnings("unused") String mode) {
+            return x.copyWithNewDimensions(null);
+        }
 
-    protected boolean castToString(String mode) {
-        return RType.Character.getName().equals(mode);
-    }
+        protected boolean castToInt(RAbstractContainer x, String mode) {
+            return x.getElementClass() != RInteger.class && RType.Integer.getName().equals(mode);
+        }
 
-    protected boolean castToRaw(RAbstractContainer x, String mode) {
-        return x.getElementClass() != RRaw.class && RType.Raw.getName().equals(mode);
-    }
+        protected boolean castToDouble(RAbstractContainer x, String mode) {
+            return x.getElementClass() != RDouble.class && (RType.Double.getClazz().equals(mode) || RType.Double.getName().equals(mode));
+        }
 
-    protected boolean castToList(String mode) {
-        return RType.List.getName().equals(mode);
-    }
+        protected boolean castToComplex(RAbstractContainer x, String mode) {
+            return x.getElementClass() != RComplex.class && RType.Complex.getName().equals(mode);
+        }
 
-    protected boolean castToSymbol(RAbstractContainer x, String mode) {
-        return x.getElementClass() != Object.class && RType.Symbol.getName().equals(mode);
-    }
+        protected boolean castToLogical(RAbstractContainer x, String mode) {
+            return x.getElementClass() != RLogical.class && RType.Logical.getName().equals(mode);
+        }
 
-    protected boolean castToExpression(String mode) {
-        return RType.Expression.getName().equals(mode);
-    }
+        protected boolean castToString(String mode) {
+            return RType.Character.getName().equals(mode);
+        }
 
-    protected boolean modeMatches(RAbstractVector x, String mode) {
-        return RRuntime.classToString(x.getElementClass()).equals(mode) || x.getElementClass() == RDouble.class && RType.Double.getName().equals(mode);
-    }
+        protected boolean castToRaw(RAbstractContainer x, String mode) {
+            return x.getElementClass() != RRaw.class && RType.Raw.getName().equals(mode);
+        }
 
-    protected boolean modeIsAny(String mode) {
-        return RType.Any.getName().equals(mode);
-    }
+        protected boolean castToList(String mode) {
+            return RType.List.getName().equals(mode);
+        }
 
-    protected boolean modeIsPairList(String mode) {
-        return RType.PairList.getName().equals(mode);
-    }
+        protected boolean castToSymbol(RAbstractContainer x, String mode) {
+            return x.getElementClass() != Object.class && RType.Symbol.getName().equals(mode);
+        }
 
-    @SuppressWarnings("unused")
-    @Fallback
-    @TruffleBoundary
-    protected RAbstractVector asVectorWrongMode(Object x, Object mode) {
-        controlVisibility();
-        throw RError.error(RError.SHOW_CALLER, RError.Message.INVALID_ARGUMENT, "mode");
+        protected boolean castToExpression(String mode) {
+            return RType.Expression.getName().equals(mode);
+        }
+
+        protected boolean modeMatches(RAbstractVector x, String mode) {
+            return RRuntime.classToString(x.getElementClass()).equals(mode) || x.getElementClass() == RDouble.class && RType.Double.getName().equals(mode);
+        }
+
+        protected boolean modeIsAny(String mode) {
+            return RType.Any.getName().equals(mode);
+        }
+
+        protected boolean modeIsPairList(String mode) {
+            return RType.PairList.getName().equals(mode);
+        }
+
+        @SuppressWarnings("unused")
+        @Fallback
+        @TruffleBoundary
+        protected RAbstractVector asVectorWrongMode(Object x, String mode) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.INVALID_ARGUMENT, "mode");
+        }
     }
 }
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 52adbf3a11dea8a445eb6363720156f718f2a1e5..1d6e7c0216b1f24f7c0f5b7e558329c11e8bba30 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
@@ -45,8 +45,6 @@ import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctions;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctionsFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRCallCounting;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRCallCountingFactory;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRCompile;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRCompileNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebug;
@@ -277,7 +275,6 @@ public class BasePackage extends RBuiltinPackage {
         add(Expression.class, ExpressionNodeGen::create);
         add(FastRCallCounting.CreateCallCounter.class, FastRCallCountingFactory.CreateCallCounterNodeGen::create);
         add(FastRCallCounting.GetCallCounter.class, FastRCallCountingFactory.GetCallCounterNodeGen::create);
-        add(FastRCompile.class, FastRCompileNodeGen::create);
         add(FastRContext.CloseChannel.class, FastRContextFactory.CloseChannelNodeGen::create);
         add(FastRContext.Create.class, FastRContextFactory.CreateNodeGen::create);
         add(FastRContext.CreateChannel.class, FastRContextFactory.CreateChannelNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
index 010d68a7e827fa350a899c70a8bfb3118c30bf2b..c541a7b83417fb90c612cd060f6240e680e7b0bd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
@@ -27,58 +27,51 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 import java.util.Arrays;
 
 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.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.nodes.builtin.RPrecedenceBuiltinNode;
-import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
-import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
 import com.oracle.truffle.r.nodes.function.UseMethodInternalNode;
-import com.oracle.truffle.r.nodes.unary.CastComplexNode;
-import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
-import com.oracle.truffle.r.nodes.unary.CastListNode;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
-import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+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.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-public abstract class Bind extends RPrecedenceBuiltinNode {
+public abstract class Bind extends RBaseNode {
+
+    protected enum BindType {
+        rbind,
+        cbind;
+    }
+
+    protected static final int NO_PRECEDENCE = PrecedenceNode.NO_PRECEDENCE;
+    protected static final int RAW_PRECEDENCE = PrecedenceNode.RAW_PRECEDENCE;
+    protected static final int LOGICAL_PRECEDENCE = PrecedenceNode.LOGICAL_PRECEDENCE;
+    protected static final int INT_PRECEDENCE = PrecedenceNode.INT_PRECEDENCE;
+    protected static final int DOUBLE_PRECEDENCE = PrecedenceNode.DOUBLE_PRECEDENCE;
+    protected static final int COMPLEX_PRECEDENCE = PrecedenceNode.COMPLEX_PRECEDENCE;
+    protected static final int STRING_PRECEDENCE = PrecedenceNode.STRING_PRECEDENCE;
+    protected static final int LIST_PRECEDENCE = PrecedenceNode.LIST_PRECEDENCE;
+    protected static final int EXPRESSION_PRECEDENCE = PrecedenceNode.EXPRESSION_PRECEDENCE;
+
+    public abstract Object execute(VirtualFrame frame, Object deparseLevelObj, Object[] args, RArgsValuesAndNames promiseArgs, int precedence);
 
     @Child private CastToVectorNode castVector;
     @Child private UseMethodInternalNode dcn;
     @Child private CastLogicalNode castLogical;
 
+    private final BindType type;
+
     private final ConditionProfile nullNamesProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile emptyVectorProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile nonNullNames = BranchProfile.create();
@@ -87,10 +80,19 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
     protected final ValueProfile resultProfile = ValueProfile.createClassProfile();
     protected final ValueProfile vectorProfile = ValueProfile.createClassProfile();
 
-    protected String getBindType() {
-        // this method should be abstract but due to annotation processor problem it does not work
-        RInternalError.unimplemented("getBindType() method must be overridden in a subclass");
-        return null;
+    @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create();
+
+    protected int precedence(RArgsValuesAndNames args) {
+        int precedence = -1;
+        Object[] array = args.getArguments();
+        for (int i = 0; i < array.length; i++) {
+            precedence = Math.max(precedence, precedenceNode.executeInteger(array[i], RRuntime.LOGICAL_FALSE));
+        }
+        return precedence;
+    }
+
+    protected Bind(BindType type) {
+        this.type = type;
     }
 
     protected RAbstractVector castVector(Object value) {
@@ -110,9 +112,8 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
     }
 
     @SuppressWarnings("unused")
-    @Specialization(guards = "isNullPrecedence(args)")
-    protected RNull allNull(VirtualFrame frame, Object deparseLevelObj, RArgsValuesAndNames args) {
-        controlVisibility();
+    @Specialization(guards = "precedence == NO_PRECEDENCE")
+    protected RNull allNull(VirtualFrame frame, Object deparseLevelObj, Object[] args, RArgsValuesAndNames promiseArgs, int precedence) {
         return RNull.instance;
     }
 
@@ -120,35 +121,33 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
 
     private static final RStringVector DATA_FRAME_CLASS = RDataFactory.createStringVectorFromScalar("data.frame");
 
-    @Specialization(guards = {"args.getLength() > 1", "isDataFrame(args)"})
-    protected Object allDataFrame(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args) {
+    @Specialization(guards = {"args.length > 1", "isDataFrame(args)"})
+    protected Object allDataFrame(VirtualFrame frame, Object deparseLevel, @SuppressWarnings("unused") Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence) {
         if (dcn == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            dcn = insert(new UseMethodInternalNode(getBindType(), SIGNATURE, false));
+            dcn = insert(new UseMethodInternalNode(type.toString(), SIGNATURE, false));
         }
         try {
-            return dcn.execute(frame, DATA_FRAME_CLASS, new Object[]{deparseLevel, args});
+            return dcn.execute(frame, DATA_FRAME_CLASS, new Object[]{deparseLevel, promiseArgs});
         } catch (S3FunctionLookupNode.NoGenericMethodException e) {
             throw RInternalError.shouldNotReachHere();
         }
     }
 
-    private Object bindInternal(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args, CastNode castNode, boolean needsVectorCast) {
-        controlVisibility();
-        Object[] array = args.getArguments();
-        ArgumentsSignature signature = args.getSignature();
+    private Object bindInternal(Object deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, CastNode castNode, boolean needsVectorCast) {
+        ArgumentsSignature signature = promiseArgs.getSignature();
         String[] vecNames = nullNamesProfile.profile(signature.getNonNullCount() == 0) ? null : new String[signature.getLength()];
-        RAbstractVector[] vectors = new RAbstractVector[args.getLength()];
+        RAbstractVector[] vectors = new RAbstractVector[args.length];
         boolean complete = true;
         int ind = 0;
         naCheck.enable(true);
-        for (int i = 0; i < array.length; i++) {
+        for (int i = 0; i < args.length; i++) {
             if (vecNames != null) {
                 nonNullNames.enter();
                 vecNames[ind] = signature.getName(i);
                 naCheck.check(vecNames[ind]);
             }
-            Object result = castNode.execute(array[i]);
+            Object result = castNode.execute(args[i]);
             RAbstractVector vector;
             if (needsVectorCast) {
                 vector = castVector(result);
@@ -163,80 +162,48 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
                 ind++;
             }
         }
-        if (emptyVectorProfile.profile(ind < array.length)) {
+        if (emptyVectorProfile.profile(ind < args.length)) {
             if (vecNames != null) {
                 nonNullNames.enter();
                 vecNames = Arrays.copyOf(vecNames, ind);
             }
             vectors = Arrays.copyOf(vectors, ind);
         }
-        return genericBind(frame, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel);
+        if (type == BindType.cbind) {
+            return genericCBind(promiseArgs, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel);
+        } else {
+            return genericRBind(promiseArgs, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel);
+        }
     }
 
-    @Specialization(guards = {"isIntegerPrecedence(args)", "args.getLength() > 1", "!isDataFrame(args)"})
-    protected Object allInt(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args, //
+    @Specialization(guards = {"precedence == INT_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
+    protected Object allInt(Object deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
                     @Cached("create()") CastIntegerNode cast) {
-        return bindInternal(frame, deparseLevel, args, cast, true);
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
     }
 
-    @Specialization(guards = {"isDoublePrecedence( args)", "args.getLength() > 1", "!isDataFrame(args)"})
-    protected Object allDouble(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args, //
+    @Specialization(guards = {"precedence == DOUBLE_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
+    protected Object allDouble(Object deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
                     @Cached("create()") CastDoubleNode cast) {
-        return bindInternal(frame, deparseLevel, args, cast, true);
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
     }
 
-    @Specialization(guards = {"isStringPrecedence( args)", "args.getLength() > 1", "!isDataFrame(args)"})
-    protected Object allString(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args, //
+    @Specialization(guards = {"precedence == STRING_PRECEDENCE", "args.length> 1", "!isDataFrame(args)"})
+    protected Object allString(Object deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
                     @Cached("create()") CastStringNode cast) {
-        return bindInternal(frame, deparseLevel, args, cast, true);
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
     }
 
-    @Specialization(guards = {"isComplexPrecedence( args)", "args.getLength() > 1", "!isDataFrame(args)"})
-    protected Object allComplex(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args, //
+    @Specialization(guards = {"precedence == COMPLEX_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
+    protected Object allComplex(Object deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
                     @Cached("create()") CastComplexNode cast) {
-        return bindInternal(frame, deparseLevel, args, cast, true);
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
     }
 
-    @Specialization(guards = {"isListPrecedence( args)", "args.getLength() > 1", "!isDataFrame(args)"})
-    protected Object allList(VirtualFrame frame, Object deparseLevel, RArgsValuesAndNames args, //
+    @Specialization(guards = {"precedence == LIST_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
+    protected Object allList(Object deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
                     @Cached("create()") CastListNode cast) {
-        return bindInternal(frame, deparseLevel, args, cast, false);
-    }
-
-    protected Object allOneElem(VirtualFrame frame, Object deparseLevelObj, RArgsValuesAndNames args, boolean cbind) {
-        RAbstractVector vec = castVector(args.getArgument(0));
-        if (vec.isMatrix()) {
-            return vec;
-        }
-        int[] dims = getDimensions(vec, cbind);
-        // for cbind dimNamesA is names for the 1st dim and dimNamesB is names for 2nd dim; for
-        // rbind the other way around
-        Object dimNamesA = vec.getNames(attrProfiles) == null ? RNull.instance : vec.getNames(attrProfiles);
-        Object dimNamesB;
-
-        ArgumentsSignature signature = args.getSignature();
-        if (signature.getNonNullCount() == 0) {
-            int deparseLevel = deparseLevel(deparseLevelObj);
-            if (deparseLevel == 0) {
-                dimNamesB = RNull.instance;
-            } else {
-                // var arg is at the first position - as in the R bind call
-                RArgsValuesAndNames varArg = (RArgsValuesAndNames) RArguments.getArgument(frame, 0);
-                String deparsedName = deparseArgName(varArg, deparseLevel, 0);
-                dimNamesB = deparsedName == RRuntime.NAMES_ATTR_EMPTY_VALUE ? RNull.instance : RDataFactory.createStringVector(deparsedName);
-            }
-        } else {
-            String[] names = new String[signature.getLength()];
-            for (int i = 0; i < names.length; i++) {
-                names[i] = signature.getName(i);
-            }
-            dimNamesB = RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR);
-        }
-
-        RVector res = (RVector) vec.copyWithNewDimensions(dims);
-        res.setDimNames(RDataFactory.createList(cbind ? new Object[]{dimNamesA, dimNamesB} : new Object[]{dimNamesB, dimNamesA}));
-        res.copyRegAttributesFrom(vec);
-        return res;
+        return bindInternal(deparseLevel, args, promiseArgs, cast, false);
     }
 
     /**
@@ -273,18 +240,9 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
     /**
      * Compute dimnames for columns (cbind) or rows (rbind) from names of vectors being combined or
      * by deparsing.
-     *
-     * @param frame
-     * @param vec
-     * @param argNames
-     * @param resDim
-     * @param oldInd
-     * @param vecInd
-     * @param deparseLevelObj
-     * @param dimNamesArray
-     * @return dimnames
      */
-    protected int getDimResultNamesFromVectors(VirtualFrame frame, RAbstractVector vec, String[] argNames, int resDim, int oldInd, int vecInd, Object deparseLevelObj, String[] dimNamesArray,
+    protected int getDimResultNamesFromVectors(RArgsValuesAndNames promiseArgs, RAbstractVector vec, String[] argNames, int resDim, int oldInd, int vecInd, Object deparseLevelObj,
+                    String[] dimNamesArray,
                     int dimNamesInd) {
         int ind = oldInd;
         if (vec.isMatrix()) {
@@ -311,8 +269,7 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
                     dimNamesArray[ind++] = RRuntime.NAMES_ATTR_EMPTY_VALUE;
                     return -ind;
                 } else {
-                    RArgsValuesAndNames varArg = (RArgsValuesAndNames) RArguments.getArgument(frame, 0);
-                    String deparsedName = deparseArgName(varArg, deparseLevel, vecInd);
+                    String deparsedName = deparseArgName(promiseArgs, deparseLevel, vecInd);
                     dimNamesArray[ind++] = deparsedName;
                     return deparsedName == RRuntime.NAMES_ATTR_EMPTY_VALUE ? -ind : ind;
                 }
@@ -331,33 +288,25 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
         }
     }
 
-    @SuppressWarnings("unused")
-    protected RVector genericBind(VirtualFrame frame, RAbstractVector[] vectors, boolean complete, String[] vacNames, boolean vecNamesComplete, Object deparseLevel) {
-        // this method should be abstract but due to annotation processor problem it does not work
-        RInternalError.unimplemented("genericBind() method must be overridden in a subclass");
-        return null;
-    }
-
     /**
      *
      * @param vectors vectors to be combined
      * @param res result dims
      * @param bindDims columns dim (cbind) or rows dim (rbind)
-     * @param cbind to be used for cbind function (true) or rbind function (false)
      * @return whether number of rows (cbind) or columns (rbind) in vectors is the same
      */
-    protected static boolean getResultDimensions(RAbstractVector[] vectors, int[] res, int[] bindDims, boolean cbind) {
-        int srcDim1Ind = cbind ? 0 : 1;
-        int srcDim2Ind = cbind ? 1 : 0;
+    protected boolean getResultDimensions(RAbstractVector[] vectors, int[] res, int[] bindDims) {
+        int srcDim1Ind = type == BindType.cbind ? 0 : 1;
+        int srcDim2Ind = type == BindType.cbind ? 1 : 0;
         assert vectors.length > 0;
-        int[] dim = getDimensions(vectors[0], cbind);
+        int[] dim = getDimensions(vectors[0]);
         assert dim.length == 2;
         bindDims[0] = dim[srcDim2Ind];
         res[srcDim1Ind] = dim[srcDim1Ind];
         res[srcDim2Ind] = dim[srcDim2Ind];
         boolean notEqualDims = false;
         for (int i = 1; i < vectors.length; i++) {
-            int[] dims = getDimensions(vectors[i], cbind);
+            int[] dims = getDimensions(vectors[i]);
             assert dims.length == 2;
             bindDims[i] = dims[srcDim2Ind];
             if (dims[srcDim1Ind] != res[srcDim1Ind]) {
@@ -380,10 +329,10 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
         }
     }
 
-    protected static int[] getDimensions(RAbstractVector vector, boolean cbind) {
+    protected int[] getDimensions(RAbstractVector vector) {
         int[] dimensions = vector.getDimensions();
         if (dimensions == null || dimensions.length != 2) {
-            return cbind ? new int[]{vector.getLength(), 1} : new int[]{1, vector.getLength()};
+            return type == BindType.cbind ? new int[]{vector.getLength(), 1} : new int[]{1, vector.getLength()};
         } else {
             assert dimensions.length == 2;
             return dimensions;
@@ -391,9 +340,9 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
     }
 
     @TruffleBoundary
-    protected static String deparseArgName(RArgsValuesAndNames varArg, int deparseLevel, int argInd) {
-        assert varArg.getLength() >= argInd;
-        Object argValue = varArg.getArgument(argInd);
+    protected static String deparseArgName(RArgsValuesAndNames promiseArgs, int deparseLevel, int argInd) {
+        assert promiseArgs.getLength() >= argInd;
+        Object argValue = promiseArgs.getArgument(argInd);
         if (argValue instanceof RPromise) {
             RPromise p = (RPromise) argValue;
             Object node = RASTUtils.createLanguageElement(RASTUtils.unwrap(p.getRep()));
@@ -409,191 +358,209 @@ public abstract class Bind extends RPrecedenceBuiltinNode {
 
     @Child private InheritsCheckNode inheritsCheck = new InheritsCheckNode(RRuntime.CLASS_DATA_FRAME);
 
-    public static final class InheritsCheckNode extends Node {
+    protected boolean isDataFrame(Object[] args) {
+        for (int i = 0; i < args.length; i++) {
+            if (inheritsCheck.execute(args[i])) {
+                return true;
+            }
+        }
 
-        @Child private ClassHierarchyNode classHierarchy = ClassHierarchyNodeGen.create(false, false);
-        private final ConditionProfile nullClassProfile = ConditionProfile.createBinaryProfile();
-        @CompilationFinal private ConditionProfile exactMatchProfile;
-        private final String checkedClazz;
+        return false;
+    }
 
-        public InheritsCheckNode(String checkedClazz) {
-            this.checkedClazz = checkedClazz;
-            assert RType.fromMode(checkedClazz) == null;
-        }
+    @RBuiltin(name = "cbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."})
+    public abstract static class CbindInternal extends RBuiltinNode {
 
-        public boolean execute(Object value) {
-            RStringVector clazz = classHierarchy.execute(value);
-            if (nullClassProfile.profile(clazz != null)) {
-                for (int j = 0; j < clazz.getLength(); ++j) {
-                    if (exactMatchProfile == null) {
-                        CompilerDirectives.transferToInterpreterAndInvalidate();
-                        exactMatchProfile = ConditionProfile.createBinaryProfile();
-                    }
-                    if (exactMatchProfile.profile(clazz.getDataAt(j) == checkedClazz) || clazz.getDataAt(j).equals(checkedClazz)) {
-                        return true;
-                    }
-                }
+        @Child private Bind bind = BindNodeGen.create(BindType.cbind);
+        @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create();
+
+        private int precedence(Object[] args) {
+            int precedence = -1;
+            for (int i = 0; i < args.length; i++) {
+                precedence = Math.max(precedence, precedenceNode.executeInteger(args[i], RRuntime.LOGICAL_FALSE));
             }
-            return false;
+            return precedence;
+        }
+
+        @Specialization
+        protected Object bind(VirtualFrame frame, Object deparseLevelObj, RArgsValuesAndNames args) {
+            controlVisibility();
+            return bind.execute(frame, deparseLevelObj, args.getArguments(), (RArgsValuesAndNames) RArguments.getArgument(frame, 0), precedence(args.getArguments()));
         }
     }
 
-    protected boolean isDataFrame(RArgsValuesAndNames args) {
-        for (int i = 0; i < args.getLength(); i++) {
-            if (inheritsCheck.execute(args.getArgument(i))) {
-                return true;
+    private final BranchProfile everSeenNotEqualRows = BranchProfile.create();
+    private final BranchProfile everSeenNotEqualColumns = BranchProfile.create();
+
+    @Specialization(guards = {"precedence != NO_PRECEDENCE", "args.length == 1"})
+    protected Object allOneElem(Object deparseLevelObj, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence) {
+        RAbstractVector vec = castVector(args[0]);
+        if (vec.isMatrix()) {
+            return vec;
+        }
+        int[] dims = getDimensions(vec);
+        // for cbind dimNamesA is names for the 1st dim and dimNamesB is names for 2nd dim; for
+        // rbind the other way around
+        Object dimNamesA = vec.getNames(attrProfiles) == null ? RNull.instance : vec.getNames(attrProfiles);
+        Object dimNamesB;
+
+        ArgumentsSignature signature = promiseArgs.getSignature();
+        if (signature.getNonNullCount() == 0) {
+            int deparseLevel = deparseLevel(deparseLevelObj);
+            if (deparseLevel == 0) {
+                dimNamesB = RNull.instance;
+            } else {
+                // var arg is at the first position - as in the R bind call
+                String deparsedName = deparseArgName(promiseArgs, deparseLevel, 0);
+                dimNamesB = deparsedName == RRuntime.NAMES_ATTR_EMPTY_VALUE ? RNull.instance : RDataFactory.createStringVector(deparsedName);
             }
+        } else {
+            String[] names = new String[signature.getLength()];
+            for (int i = 0; i < names.length; i++) {
+                names[i] = signature.getName(i);
+            }
+            dimNamesB = RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR);
         }
 
-        return false;
+        RVector res = (RVector) vec.copyWithNewDimensions(dims);
+        res.setDimNames(RDataFactory.createList(type == BindType.cbind ? new Object[]{dimNamesA, dimNamesB} : new Object[]{dimNamesB, dimNamesA}));
+        res.copyRegAttributesFrom(vec);
+        return res;
     }
 
-    @RBuiltin(name = "cbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."})
-    public abstract static class CbindInternal extends Bind {
-        private final BranchProfile everSeenNotEqualRows = BranchProfile.create();
+    public RVector genericCBind(RArgsValuesAndNames promiseArgs, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, Object deparseLevel) {
 
-        @Override
-        public String getBindType() {
-            return "cbind";
-        }
+        int[] resultDimensions = new int[2];
+        int[] secondDims = new int[vectors.length];
+        boolean notEqualRows = getResultDimensions(vectors, resultDimensions, secondDims);
+        RVector result = resultProfile.profile(vectors[0].createEmptySameType(resultDimensions[0] * resultDimensions[1], complete));
 
-        @Specialization(guards = {"!isNullPrecedence(args)", "args.getLength() == 1"})
-        protected Object allOneElem(VirtualFrame frame, Object deparseLevelObj, RArgsValuesAndNames args) {
-            controlVisibility();
-            return allOneElem(frame, deparseLevelObj, args, true);
-        }
+        int ind = 0;
+        Object rowDimResultNames = RNull.instance;
+        String[] colDimNamesArray = new String[resultDimensions[1]];
+        int colInd = 0;
+        boolean allColDimNamesNull = true;
+        for (int i = 0; i < vectors.length; i++) {
+            RAbstractVector vec = vectorProfile.profile(vectors[i]);
+            if (rowDimResultNames == RNull.instance) {
+                // get the first valid names value
+                rowDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[0], 0);
+            }
 
-        @Override
-        public RVector genericBind(VirtualFrame frame, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, Object deparseLevel) {
-
-            int[] resultDimensions = new int[2];
-            int[] secondDims = new int[vectors.length];
-            boolean notEqualRows = getResultDimensions(vectors, resultDimensions, secondDims, true);
-            RVector result = resultProfile.profile(vectors[0].createEmptySameType(resultDimensions[0] * resultDimensions[1], complete));
-
-            int ind = 0;
-            Object rowDimResultNames = RNull.instance;
-            String[] colDimNamesArray = new String[resultDimensions[1]];
-            int colInd = 0;
-            boolean allColDimNamesNull = true;
-            for (int i = 0; i < vectors.length; i++) {
-                RAbstractVector vec = vectorProfile.profile(vectors[i]);
-                if (rowDimResultNames == RNull.instance) {
-                    // get the first valid names value
-                    rowDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[0], 0);
-                }
+            // compute dimnames for the second dimension
+            int newColInd = getDimResultNamesFromVectors(promiseArgs, vec, vecNames, secondDims[i], colInd, i, deparseLevel, colDimNamesArray, 1);
+            if (newColInd < 0) {
+                colInd = -newColInd;
+            } else {
+                allColDimNamesNull = false;
+                colInd = newColInd;
+            }
 
-                // compute dimnames for the second dimension
-                int newColInd = getDimResultNamesFromVectors(frame, vec, vecNames, secondDims[i], colInd, i, deparseLevel, colDimNamesArray, 1);
-                if (newColInd < 0) {
-                    colInd = -newColInd;
-                } else {
-                    allColDimNamesNull = false;
-                    colInd = newColInd;
-                }
+            // compute result vector values
+            int vecLength = vec.getLength();
+            for (int j = 0; j < vecLength; j++) {
+                result.transferElementSameType(ind++, vec, j);
+            }
+            if (notEqualRows) {
+                everSeenNotEqualRows.enter();
+                if (vecLength < resultDimensions[0]) {
+                    // re-use vector elements
+                    int k = 0;
+                    for (int j = 0; j < resultDimensions[0] - vecLength; j++, k = Utils.incMod(k, vecLength)) {
+                        result.transferElementSameType(ind++, vectors[i], k);
+                    }
 
-                // compute result vector values
-                int vecLength = vec.getLength();
-                for (int j = 0; j < vecLength; j++) {
-                    result.transferElementSameType(ind++, vec, j);
-                }
-                if (notEqualRows) {
-                    everSeenNotEqualRows.enter();
-                    if (vecLength < resultDimensions[0]) {
-                        // re-use vector elements
-                        int k = 0;
-                        for (int j = 0; j < resultDimensions[0] - vecLength; j++, k = Utils.incMod(k, vecLength)) {
-                            result.transferElementSameType(ind++, vectors[i], k);
-                        }
-
-                        if (k != 0) {
-                            RError.warning(this, RError.Message.ROWS_NOT_MULTIPLE, i + 1);
-                        }
+                    if (k != 0) {
+                        RError.warning(this, RError.Message.ROWS_NOT_MULTIPLE, i + 1);
                     }
                 }
-
             }
-            Object colDimResultNames = allColDimNamesNull ? RNull.instance : RDataFactory.createStringVector(colDimNamesArray, vecNamesComplete);
-            result.setDimensions(resultDimensions);
-            result.setDimNames(RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
-            return result;
+
         }
+        Object colDimResultNames = allColDimNamesNull ? RNull.instance : RDataFactory.createStringVector(colDimNamesArray, vecNamesComplete);
+        result.setDimensions(resultDimensions);
+        result.setDimNames(RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
+        return result;
     }
 
     @RBuiltin(name = "rbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."})
-    public abstract static class RbindInternal extends Bind {
-        private final BranchProfile everSeenNotEqualColumns = BranchProfile.create();
+    public abstract static class RbindInternal extends RBuiltinNode {
 
-        @Override
-        public String getBindType() {
-            return "rbind";
+        @Child private Bind bind = BindNodeGen.create(BindType.rbind);
+        @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create();
+
+        private int precedence(Object[] args) {
+            int precedence = -1;
+            for (int i = 0; i < args.length; i++) {
+                precedence = Math.max(precedence, precedenceNode.executeInteger(args[i], RRuntime.LOGICAL_FALSE));
+            }
+            return precedence;
         }
 
-        @Specialization(guards = {"!isNullPrecedence(args)", "args.getLength() == 1"})
-        protected Object allOneElem(VirtualFrame frame, Object deparseLevelObj, RArgsValuesAndNames args) {
+        @Specialization
+        protected Object bind(VirtualFrame frame, Object deparseLevelObj, RArgsValuesAndNames args) {
             controlVisibility();
-            return allOneElem(frame, deparseLevelObj, args, false);
+            return bind.execute(frame, deparseLevelObj, args.getArguments(), (RArgsValuesAndNames) RArguments.getArgument(frame, 0), precedence(args.getArguments()));
         }
+    }
 
-        @Override
-        public RVector genericBind(VirtualFrame frame, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, Object deparseLevel) {
-
-            int[] resultDimensions = new int[2];
-            int[] firstDims = new int[vectors.length];
-            boolean notEqualColumns = getResultDimensions(vectors, resultDimensions, firstDims, false);
-            RVector result = resultProfile.profile(vectors[0].createEmptySameType(resultDimensions[0] * resultDimensions[1], complete));
-
-            Object colDimResultNames = RNull.instance;
-            String[] rowDimNamesArray = new String[resultDimensions[0]];
-            int rowInd = 0;
-            boolean allRowDimNamesNull = true;
-            int dstRowInd = 0;
-            for (int i = 0; i < vectors.length; i++) {
-                RAbstractVector vec = vectorProfile.profile(vectors[i]);
-                if (colDimResultNames == RNull.instance) {
-                    // get the first valid names value
-                    colDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[1], 1);
-                }
+    public RVector genericRBind(RArgsValuesAndNames promiseArgs, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, Object deparseLevel) {
+
+        int[] resultDimensions = new int[2];
+        int[] firstDims = new int[vectors.length];
+        boolean notEqualColumns = getResultDimensions(vectors, resultDimensions, firstDims);
+        RVector result = resultProfile.profile(vectors[0].createEmptySameType(resultDimensions[0] * resultDimensions[1], complete));
+
+        Object colDimResultNames = RNull.instance;
+        String[] rowDimNamesArray = new String[resultDimensions[0]];
+        int rowInd = 0;
+        boolean allRowDimNamesNull = true;
+        int dstRowInd = 0;
+        for (int i = 0; i < vectors.length; i++) {
+            RAbstractVector vec = vectorProfile.profile(vectors[i]);
+            if (colDimResultNames == RNull.instance) {
+                // get the first valid names value
+                colDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[1], 1);
+            }
 
-                // compute dimnames for the second dimension
-                int newRowInd = getDimResultNamesFromVectors(frame, vec, vecNames, firstDims[i], rowInd, i, deparseLevel, rowDimNamesArray, 0);
-                if (newRowInd < 0) {
-                    rowInd = -newRowInd;
-                } else {
-                    allRowDimNamesNull = false;
-                    rowInd = newRowInd;
-                }
+            // compute dimnames for the second dimension
+            int newRowInd = getDimResultNamesFromVectors(promiseArgs, vec, vecNames, firstDims[i], rowInd, i, deparseLevel, rowDimNamesArray, 0);
+            if (newRowInd < 0) {
+                rowInd = -newRowInd;
+            } else {
+                allRowDimNamesNull = false;
+                rowInd = newRowInd;
+            }
 
-                // compute result vector values
-                int vecLength = vec.getLength();
-                int srcInd = 0;
-                int j = 0;
-                for (; j < vecLength / firstDims[i]; j++) {
-                    for (int k = dstRowInd; k < dstRowInd + firstDims[i]; k++) {
-                        result.transferElementSameType(j * resultDimensions[0] + k, vec, srcInd++);
-                    }
+            // compute result vector values
+            int vecLength = vec.getLength();
+            int srcInd = 0;
+            int j = 0;
+            for (; j < vecLength / firstDims[i]; j++) {
+                for (int k = dstRowInd; k < dstRowInd + firstDims[i]; k++) {
+                    result.transferElementSameType(j * resultDimensions[0] + k, vec, srcInd++);
                 }
-                if (notEqualColumns) {
-                    everSeenNotEqualColumns.enter();
-                    if (j < resultDimensions[1]) {
-                        // re-use vector elements
-                        int k = 0;
-                        for (; j < resultDimensions[1]; j++, k = Utils.incMod(k, vecLength % resultDimensions[1])) {
-                            result.transferElementSameType(j * resultDimensions[0] + (dstRowInd + firstDims[i] - 1), vectors[i], k);
-                        }
-
-                        if (k != 0) {
-                            RError.warning(this, RError.Message.COLUMNS_NOT_MULTIPLE, i + 1);
-                        }
+            }
+            if (notEqualColumns) {
+                everSeenNotEqualColumns.enter();
+                if (j < resultDimensions[1]) {
+                    // re-use vector elements
+                    int k = 0;
+                    for (; j < resultDimensions[1]; j++, k = Utils.incMod(k, vecLength % resultDimensions[1])) {
+                        result.transferElementSameType(j * resultDimensions[0] + (dstRowInd + firstDims[i] - 1), vectors[i], k);
                     }
-                }
-                dstRowInd += firstDims[i];
 
+                    if (k != 0) {
+                        RError.warning(this, RError.Message.COLUMNS_NOT_MULTIPLE, i + 1);
+                    }
+                }
             }
-            Object rowDimResultNames = allRowDimNamesNull ? RNull.instance : RDataFactory.createStringVector(rowDimNamesArray, vecNamesComplete);
-            result.setDimensions(resultDimensions);
-            result.setDimNames(RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
-            return result;
+            dstRowInd += firstDims[i];
+
         }
+        Object rowDimResultNames = allRowDimNamesNull ? RNull.instance : RDataFactory.createStringVector(rowDimNamesArray, vecNamesComplete);
+        result.setDimensions(resultDimensions);
+        result.setDimNames(RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
+        return result;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
index 101c464098932c834c544d786422577517ef74d6..2d4ed6aa7be1ea01176849c374d913dfbc82c11f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.r.nodes.builtin.RInvisibleBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -45,11 +44,4 @@ public abstract class CopyDFAttr extends RInvisibleBuiltinNode {
         res.resetAllAttributes(false);
         return res.copyAttributesFrom(attrProfiles, in);
     }
-
-    @Specialization()
-    protected RAttributable copy(RAbstractContainer in, RFactor out) {
-        RVector res = out.getVector();
-        res.resetAllAttributes(false);
-        return res.copyAttributesFrom(attrProfiles, in);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
index 23a3b7157753b159e41743dee462c7fc65b2dbf2..269a3a17a3ca5e0e54c063e6cc907ad9ce5fdefb 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
@@ -26,12 +26,10 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
 
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
@@ -41,9 +39,6 @@ public abstract class DimNames extends RBuiltinNode {
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final ConditionProfile nullProfile = ConditionProfile.createBinaryProfile();
-    private final BranchProfile dataframeProfile = BranchProfile.create();
-    private final BranchProfile factorProfile = BranchProfile.create();
-    private final BranchProfile otherProfile = BranchProfile.create();
 
     @Specialization
     protected RNull getDimNames(@SuppressWarnings("unused") RNull operand) {
@@ -54,14 +49,7 @@ public abstract class DimNames extends RBuiltinNode {
     @Specialization
     protected Object getDimNames(RAbstractContainer container) {
         controlVisibility();
-        RList names;
-        if (container instanceof RFactor) {
-            factorProfile.enter();
-            names = ((RFactor) container).getVector().getDimNames();
-        } else {
-            otherProfile.enter();
-            names = container.getDimNames(attrProfiles);
-        }
+        RList names = container.getDimNames(attrProfiles);
         return nullProfile.profile(names == null) ? RNull.instance : names;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
index c3136361e36c15445c5d4912009641c6f641f233..f1828f586f2ec87bd74c3ae75c76ccec60e0ee65 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
@@ -25,48 +25,30 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 
 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.Specialization;
+import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.r.nodes.CallInlineCacheNode;
-import com.oracle.truffle.r.nodes.CallInlineCacheNodeGen;
 import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.nodes.access.ConstantNode;
+import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
-import com.oracle.truffle.r.nodes.function.ArgumentMatcher;
-import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
-import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
-import com.oracle.truffle.r.nodes.function.EvaluatedArguments;
-import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
-import com.oracle.truffle.r.nodes.function.RCallNode.RootCallNode;
-import com.oracle.truffle.r.nodes.function.RCallerHelper;
-import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
-import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
-import com.oracle.truffle.r.nodes.function.signature.GetCallerFrameNode;
-import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
 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.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
@@ -79,26 +61,18 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 @RBuiltin(name = "do.call", kind = INTERNAL, parameterNames = {"what", "args", "envir"})
 public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNodeChildren {
 
-    @Child private CallInlineCacheNode callCache = CallInlineCacheNodeGen.create();
     @Child private GetFunctions.Get getNode;
-    @Child private GroupDispatchNode groupDispatch;
     @Child private GetCallerFrameNode getCallerFrame;
 
-    @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode();
-    @CompilationFinal private boolean needsCallerFrame;
-
-    @Child private S3FunctionLookupNode dispatchLookup;
-    @Child private PromiseCheckHelperNode hierarchyPromiseHelper;
-    @Child private ClassHierarchyNode classHierarchyNode;
-    @Child private RootCallNode internalDispatchCall;
-
-    @Child private RArgumentsNode argsNode = RArgumentsNode.create();
-
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile errorProfile = BranchProfile.create();
     private final BranchProfile containsRLanguageProfile = BranchProfile.create();
     private final BranchProfile containsRSymbolProfile = BranchProfile.create();
 
+    private final Object argsIdentifier = new Object();
+    @Child private RCallNode call = RCallNode.createExplicitCall(argsIdentifier);
+    @Child private FrameSlotNode slot = FrameSlotNode.createTemp(argsIdentifier, true);
+
     @Specialization
     protected Object doDoCall(VirtualFrame frame, Object what, RList argsAsList, REnvironment env) {
         /*
@@ -155,85 +129,9 @@ public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNode
                 }
             }
         }
-
-        /* Step 3: Perform the actual evaluation, checking for builtin special cases. */
-        return executeCall(frame, func, argValues, signature, callerFrame);
-    }
-
-    /* This exists solely for forceAndCall builtin (R3.2.3) */
-    @Specialization
-    protected Object doDoCall(VirtualFrame frame, RFunction func, RArgsValuesAndNames args, @SuppressWarnings("unused") RNull env) {
-        return executeCall(frame, func, args.getArguments(), args.getSignature(), null);
-    }
-
-    private Object executeCall(VirtualFrame frame, RFunction funcArg, Object[] argValues, ArgumentsSignature signature, MaterializedFrame callerFrameArg) {
-        RFunction func = funcArg;
-        MaterializedFrame callerFrame = callerFrameArg;
-        RBuiltinDescriptor builtin = func.getRBuiltin();
-        if (func.isBuiltin() && builtin.getDispatch() == RDispatch.INTERNAL_GENERIC) {
-            if (dispatchLookup == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                dispatchLookup = insert(S3FunctionLookupNode.create(true, false));
-                classHierarchyNode = insert(ClassHierarchyNodeGen.create(true, true));
-                hierarchyPromiseHelper = insert(new PromiseCheckHelperNode());
-            }
-            RStringVector type = classHierarchyNode.execute(hierarchyPromiseHelper.checkEvaluate(frame, argValues[0]));
-            Result result = dispatchLookup.execute(frame, builtin.getName(), type, null, frame.materialize(), null);
-            if (result != null) {
-                func = result.function;
-            }
-        }
-        if (func.isBuiltin() && builtin.getDispatch() != null && builtin.getDispatch().isGroupGeneric()) {
-            if (groupDispatch == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                /* This child is not being used in a syntax context so remove tags */
-                groupDispatch = insert(GroupDispatchNode.create(builtin.getName(), null, func, getOriginalCall().getSourceSection()));
-            }
-            for (int i = 0; i < argValues.length; i++) {
-                Object arg = argValues[i];
-                if (arg instanceof RPromise) {
-                    argValues[i] = promiseHelper.evaluate(frame, (RPromise) arg);
-                }
-            }
-            return groupDispatch.executeDynamic(frame, new RArgsValuesAndNames(argValues, signature), builtin.getName().intern(), builtin.getDispatch(), func);
-        }
-        EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, signature);
-        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(func, evaledArgs, this, false);
-        if (func.isBuiltin()) {
-            Object[] argArray = reorderedArgs.getArguments();
-            for (int i = 0; i < argArray.length; i++) {
-                Object arg = argArray[i];
-                if (builtin.evaluatesArg(i)) {
-                    if (arg instanceof RPromise) {
-                        argArray[i] = promiseHelper.evaluate(frame, (RPromise) arg);
-                    } else if (arg instanceof RArgsValuesAndNames) {
-                        Object[] varArgValues = ((RArgsValuesAndNames) arg).getArguments();
-                        for (int j = 0; j < varArgValues.length; j++) {
-                            if (varArgValues[j] instanceof RPromise) {
-                                varArgValues[j] = promiseHelper.evaluate(frame, (RPromise) varArgValues[j]);
-                            }
-                        }
-                    }
-                } else {
-                    if (!(arg instanceof RPromise) && arg != RMissing.instance && !(arg instanceof RArgsValuesAndNames)) {
-                        callerFrame = getCallerFrame(frame, callerFrame);
-                        argArray[i] = createArgPromise(callerFrame, ConstantNode.create(arg));
-                    }
-                }
-            }
-        }
-        if (!needsCallerFrame && func.containsDispatch()) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            needsCallerFrame = true;
-        }
-        callerFrame = needsCallerFrame ? getCallerFrame(frame, callerFrame) : null;
-        Object[] callArgs = argsNode.execute(func, new RCallerHelper.Representation(func, argValues), callerFrame, RArguments.getDepth(frame) + 1,
-                        RArguments.getPromiseFrame(frame),
-                        reorderedArgs.getArguments(), reorderedArgs.getSignature(),
-                        null);
-        RArguments.setIsIrregular(callArgs, true);
-        return callCache.execute(frame, func.getTarget(), callArgs);
-
+        FrameSlot frameSlot = slot.executeFrameSlot(frame);
+        frame.setObject(frameSlot, new RArgsValuesAndNames(argValues, signature));
+        return call.execute(frame, func);
     }
 
     private MaterializedFrame getCallerFrame(VirtualFrame frame, MaterializedFrame callerFrame) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
index d6cc39970113f6710dc95d28f58cfa00b43e2a59..289e049b082d60bafa210a567f341f4c93019d83 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
@@ -47,9 +47,9 @@ import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
 import com.oracle.truffle.r.nodes.builtin.RList2EnvNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.EnvFunctionsFactory.CopyNodeGen;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
+import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseDeoptimizeFrameNode;
-import com.oracle.truffle.r.nodes.function.signature.GetCallerFrameNode;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
@@ -170,7 +170,7 @@ public class EnvFunctions {
         @Specialization
         protected Object asEnvironment(RS4Object obj) {
             // generic dispatch tried already
-            Object xData = obj.getAttribute(RRuntime.DOT_XDATA);
+            Object xData = obj.getAttr(RRuntime.DOT_XDATA);
             if (xData == null || !(xData instanceof REnvironment)) {
                 throw RError.error(this, RError.Message.S4OBJECT_NX_ENVIRONMENT);
             } else {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
index 7d8fdb265ffa1463ea75894a9dff0e42056059de..f93bc9ca83a5e5ab2d4a9951eaf3da03d45dc2a1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
@@ -28,22 +28,28 @@ import com.oracle.truffle.api.CompilerDirectives;
 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.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 
 @RBuiltin(name = "forceAndCall", kind = PRIMITIVE, parameterNames = {"n", "FUN", "..."}, nonEvalArgs = 2)
 public abstract class ForceAndCall extends RBuiltinNode {
 
-    @Child private DoCall doCallNode;
+    private final Object argsIdentifier = new Object();
+
+    @Child private RCallNode call = RCallNode.createExplicitCall(argsIdentifier);
+    @Child private FrameSlotNode slot = FrameSlotNode.createTemp(argsIdentifier, true);
+
     @Child private PromiseHelperNode promiseHelper;
 
     protected PromiseHelperNode initPromiseHelper() {
@@ -59,29 +65,22 @@ public abstract class ForceAndCall extends RBuiltinNode {
         casts.toInteger(0);
     }
 
-    private DoCall getDoCallNode() {
-        if (doCallNode == null) {
-            doCallNode = insert(DoCallNodeGen.create(null));
+    @Specialization
+    protected Object forceAndCallBuiltin(VirtualFrame frame, int n, RFunction fun, RArgsValuesAndNames args) {
+        if (!fun.isBuiltin()) {
+            initPromiseHelper();
+            RArgsValuesAndNames flattened = flatten(args);
+            // In GnuR there appears to be no error checks on n > args.length
+            int cnt = Math.min(flattened.getLength(), n);
+            for (int i = 0; i < cnt; i++) {
+                RPromise arg = (RPromise) args.getArgument(i);
+                initPromiseHelper().evaluate(frame, arg);
+            }
         }
-        return doCallNode;
-    }
 
-    @Specialization(guards = "isBuiltin(fun)")
-    protected Object forceAndCallBuiltin(VirtualFrame frame, @SuppressWarnings("unused") int n, RFunction fun, RArgsValuesAndNames args) {
-        return getDoCallNode().execute(frame, fun, args, RNull.instance);
-    }
-
-    @Specialization(guards = "!isBuiltin(fun)")
-    protected Object forceAndCall(VirtualFrame frame, int n, RFunction fun, RArgsValuesAndNames args) {
-        initPromiseHelper();
-        RArgsValuesAndNames flattened = flatten(args);
-        // In GnuR there appears to be no error checks on n > args.length
-        int cnt = Math.min(flattened.getLength(), n);
-        for (int i = 0; i < cnt; i++) {
-            RPromise arg = (RPromise) args.getArgument(i);
-            initPromiseHelper().evaluate(frame, arg);
-        }
-        return getDoCallNode().execute(frame, fun, flattened, RNull.instance);
+        FrameSlot frameSlot = slot.executeFrameSlot(frame);
+        frame.setObject(frameSlot, args);
+        return call.execute(frame, fun);
     }
 
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
index f6abfbfb732aab168b3a772697c8d145f103862c..9d47816e0c14736baf9d583785dcc0a327adf998 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
@@ -13,16 +13,20 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.dsl.*;
-import com.oracle.truffle.api.profiles.*;
-import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.printer.AnyVectorToStringVectorWriter;
 import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNode;
 import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNodeGen;
-import com.oracle.truffle.r.nodes.unary.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.context.*;
+import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
+import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
+import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
@@ -157,12 +161,6 @@ public abstract class Format extends RBuiltinNode {
         return value.materialize();
     }
 
-    @Specialization
-    protected RStringVector format(RFactor value, RLogicalVector trimVec, RIntVector digitsVec, RIntVector nsmallVec, RIntVector widthVec, RIntVector justifyVec, RLogicalVector naEncodeVec,
-                    RAbstractVector sciVec, RAbstractStringVector decimalMark) {
-        return format(value.getVector(), trimVec, digitsVec, nsmallVec, widthVec, justifyVec, naEncodeVec, sciVec, decimalMark);
-    }
-
     // TruffleDSL bug - should not need multiple guards here
     protected void checkArgs(RLogicalVector trimVec, RIntVector digitsVec, RIntVector nsmallVec, RIntVector widthVec, RIntVector justifyVec, RLogicalVector naEncodeVec, RAbstractVector sciVec,
                     RAbstractStringVector decimalMark) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
index 6692048fd2bec706d00e11fed9dc42404396b3b0..f69729837e082b5e82f1287bb909725f79ecfab1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
@@ -47,16 +47,15 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.ArgumentMatcher;
 import com.oracle.truffle.r.nodes.function.CallArgumentsNode;
-import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
-import com.oracle.truffle.r.nodes.function.MatchedArguments;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseDeoptimizeFrameNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgsPromiseNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.nodes.function.UnrolledVariadicArguments;
+import com.oracle.truffle.r.nodes.function.UnmatchedArguments;
 import com.oracle.truffle.r.nodes.function.signature.FrameDepthNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.HasSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RBuiltin;
@@ -260,10 +259,10 @@ public class FrameFunctions {
              */
             RCallNode callNode = (RCallNode) RASTUtils.unwrap(call.getRep());
             CallArgumentsNode callArgs = callNode.createArguments(null, false, false);
-            UnrolledVariadicArguments executeFlatten = callArgs.executeFlatten(cframe);
-            MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(definition, executeFlatten, null, true);
-            ArgumentsSignature sig = matchedArgs.getSignature();
-            RNode[] matchedArgNodes = matchedArgs.getArguments();
+            ArgumentsSignature inputVarArgSignature = callArgs.containsVarArgsSymbol() ? CallArgumentsNode.getVarargsAndNames(cframe).getSignature() : null;
+            UnmatchedArguments executeFlatten = callArgs.unrollArguments(inputVarArgSignature);
+            RNode[] matchedArgNodes = ArgumentMatcher.matchArguments(definition, executeFlatten, null, true);
+            ArgumentsSignature sig = ((HasSignature) definition.getRootNode()).getSignature();
             // expand any varargs
             ArrayList<RNode> nodes = new ArrayList<>();
             ArrayList<String> names = new ArrayList<>();
@@ -406,7 +405,7 @@ public class FrameFunctions {
             if (callObj instanceof RLanguage) {
                 RLanguage call = (RLanguage) callObj;
                 RNode node = (RNode) RASTUtils.unwrap(call.getRep());
-                if (node instanceof RCallNode || node instanceof GroupDispatchNode) {
+                if (node instanceof RCallNode) {
                     return call;
                 }
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
index d302e0459de0d2dc674180e6929bf20bcd547976..f69af929c49b70b59219713690c75394a39ceeac 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
@@ -41,11 +41,13 @@ import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallerHelper;
 import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -293,7 +295,8 @@ public class GetFunctions {
             }
             MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
             FormalArguments formals = ((RRootNode) ifnFunc.getRootNode()).getFormalArguments();
-            Object[] callArgs = argsNode.execute(ifnFunc, new RCallerHelper.Representation(ifnFunc, new Object[]{x}), callerFrame, RArguments.getDepth(frame) + 1,
+            RArgsValuesAndNames args = new RArgsValuesAndNames(new Object[]{x}, ArgumentsSignature.empty(1));
+            Object[] callArgs = argsNode.execute(ifnFunc, new RCallerHelper.Representation(ifnFunc, args), callerFrame, RArguments.getDepth(frame) + 1,
                             RArguments.getPromiseFrame(frame), new Object[]{x}, formals.getSignature(), null);
             return callCache.execute(frame, ifnFunc.getTarget(), callArgs);
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
index 3cafe87eb6cba1625f4fc0f7feb186bbe4c4a558..f1dfce08df908a60d2d230e92b8b0d97f69fb3a6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
@@ -37,18 +37,8 @@ import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributes;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -273,14 +263,6 @@ public abstract class Identical extends RBuiltinNode {
         return identicalAttr(x, y, numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
     }
 
-    @Specialization
-    protected byte doInternalIdenticalGeneric(RFactor x, RFactor y, boolean numEq, boolean singleNA, boolean attribAsSet, boolean ignoreBytecode, boolean ignoreEnvironment) {
-        if (!recursive) {
-            controlVisibility();
-        }
-        return doInternalIdenticalGeneric(x.getVector(), y.getVector(), numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
-    }
-
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdenticalGeneric(RFunction x, RAbstractContainer y, boolean numEq, boolean singleNA, boolean attribAsSet, boolean ignoreBytecode, boolean ignoreEnvironment) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
index 5d49715dff6ec09ec62e8143229377e56aeeb2f1..ef3b28c279d6a831e30e80ec16fd847fc5f5ccb7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
@@ -25,7 +25,6 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.NodeCost;
@@ -35,7 +34,6 @@ import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.nodes.function.RCallNode.LeafCallNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
@@ -66,8 +64,7 @@ public abstract class Internal extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
 
-    @Child private LeafCallNode builtinCallNode;
-    @CompilationFinal private RFunction builtinFunction;
+    @Child private RNode builtinCallNode;
 
     @Specialization
     protected Object doInternal(@SuppressWarnings("unused") RMissing x) {
@@ -101,10 +98,9 @@ public abstract class Internal extends RBuiltinNode {
 
             // .Internal function is validated
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            builtinCallNode = insert(RCallNode.createInternalCall(frame, callNode, function, name));
-            builtinFunction = function;
+            builtinCallNode = insert(RCallNode.createInternalCall(callNode, function));
         }
-        return builtinCallNode.execute(frame, builtinFunction, null);
+        return builtinCallNode.execute(frame);
     }
 
     private static boolean notImplemented(String name) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
index 5d2cc51956505cc994123bccef5f81f17b6bdd74..329afd4da9c4d1ed10e68d1d3d4fff2390a54c8a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
@@ -29,25 +29,14 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-@RBuiltin(name = "is.na", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "is.na", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.INTERNAL_GENERIC)
 public abstract class IsNA extends RBuiltinNode {
 
     @Child private IsNA recursiveIsNA;
@@ -172,11 +161,6 @@ public abstract class IsNA extends RBuiltinNode {
         return RRuntime.asLogical(RRuntime.isNA(value));
     }
 
-    @Specialization
-    protected RLogicalVector isNA(RFactor value) {
-        return isNA(value.getVector());
-    }
-
     @Specialization
     protected byte isNA(@SuppressWarnings("unused") RRaw value) {
         controlVisibility();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
index 0d01a9641fc5ad1dd2bdfd3fac46295c79e0ae5a..34efd3d0d0c0ea36340c68c51bd4342d447c85cf 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
@@ -30,32 +30,10 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-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.RPairList;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+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.nodes.RNode;
 
 /**
@@ -121,12 +99,6 @@ public class IsTypeFunctions {
             return RRuntime.LOGICAL_TRUE;
         }
 
-        @Specialization
-        protected byte isRecursive(RFactor arg) {
-            controlVisibility();
-            return RRuntime.LOGICAL_FALSE;
-        }
-
         protected boolean isListVector(RAbstractVector arg) {
             return arg instanceof RList;
         }
@@ -141,6 +113,8 @@ public class IsTypeFunctions {
     @RBuiltin(name = "is.atomic", kind = PRIMITIVE, parameterNames = {"x"})
     public abstract static class IsAtomic extends MissingAdapter {
 
+        @Child private InheritsCheckNode inheritsFactorCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
+
         @Specialization
         protected byte isAtomic(RNull arg) {
             controlVisibility();
@@ -153,18 +127,16 @@ public class IsTypeFunctions {
             return RRuntime.LOGICAL_TRUE;
         }
 
-        @Specialization
-        protected byte isAtomic(RFactor arg) {
-            controlVisibility();
-            return RRuntime.LOGICAL_TRUE;
-        }
-
         protected static boolean isNonListVector(Object value) {
             return value instanceof Integer || value instanceof Double || value instanceof RComplex || value instanceof String || value instanceof RRaw ||
                             (value instanceof RAbstractVector && !(value instanceof RList));
         }
 
-        @Specialization(guards = {"!isRMissing(value)", "!isRNull(value)", "!isRFactor(value)", "!isNonListVector(value)"})
+        protected boolean isFactor(Object value) {
+            return inheritsFactorCheck.execute(value);
+        }
+
+        @Specialization(guards = {"!isRMissing(value)", "!isRNull(value)", "!isFactor(value)", "!isNonListVector(value)"})
         protected byte isType(Object value) {
             controlVisibility();
             return RRuntime.LOGICAL_FALSE;
@@ -403,12 +375,18 @@ public class IsTypeFunctions {
     @RBuiltin(name = "is.numeric", kind = PRIMITIVE, parameterNames = {"x"})
     public abstract static class IsNumeric extends MissingAdapter {
 
-        @Specialization
+        @Specialization(guards = "!isFactor(value)")
         protected byte isType(RAbstractIntVector value) {
             controlVisibility();
             return RRuntime.LOGICAL_TRUE;
         }
 
+        @Specialization(guards = "isFactor(value)")
+        protected byte isTypeFactor(RAbstractIntVector value) {
+            controlVisibility();
+            return RRuntime.LOGICAL_FALSE;
+        }
+
         @Specialization
         protected byte isType(RAbstractDoubleVector value) {
             controlVisibility();
@@ -424,6 +402,12 @@ public class IsTypeFunctions {
             controlVisibility();
             return RRuntime.LOGICAL_FALSE;
         }
+
+        @Child private InheritsCheckNode inheritsCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
+
+        protected boolean isFactor(Object o) {
+            return inheritsCheck.execute(o);
+        }
     }
 
     @RBuiltin(name = "is.null", kind = PRIMITIVE, parameterNames = {"x"})
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
index c1a5a755da1c6a80ffa7b4aea176e8b8b198e506..830ee2595ed20a68d9fdf3e9c74b04211d48acc8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
@@ -13,40 +13,34 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.LoopNode;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
+import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
+import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.LapplyNodeGen.LapplyInternalNodeGen;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
-import com.oracle.truffle.r.nodes.function.PromiseNode;
+import com.oracle.truffle.r.nodes.control.RLengthNode;
+import com.oracle.truffle.r.nodes.control.RLengthNodeGen;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.AnonymousFrameVariable;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
-import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
-import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
  * The {@code lapply} builtin. {@code lapply} is an important implicit iterator in R. This
@@ -64,13 +58,9 @@ public abstract class Lapply extends RBuiltinNode {
 
     @Child private LapplyInternalNode lapply = LapplyInternalNodeGen.create();
 
-    @Child private PromiseCheckHelperNode promiseCheck;
-
     @Specialization
     protected Object lapply(VirtualFrame frame, RAbstractVector vec, RFunction fun) {
-        RArgsValuesAndNames optionalArgs = (RArgsValuesAndNames) RArguments.getArgument(frame, 2);
-        // forceVarArgs(frame, optionalArgs);
-        Object[] result = lapply.execute(frame, vec, fun, optionalArgs);
+        Object[] result = lapply.execute(frame, vec, fun);
         // set here else it gets overridden by the iterator evaluation
         controlVisibility();
         return RDataFactory.createList(result, vec.getNames(attrProfiles));
@@ -78,108 +68,41 @@ public abstract class Lapply extends RBuiltinNode {
 
     public abstract static class LapplyInternalNode extends RBaseNode implements InternalRSyntaxNodeChildren {
 
-        private static final String INDEX_NAME = AnonymousFrameVariable.create("LAPPLY_ITER_INDEX");
         private static final String VECTOR_ELEMENT = AnonymousFrameVariable.create("LAPPLY_VEC_ELEM");
 
-        @Child private Length lengthNode = LengthNodeGen.create(null);
+        @Child private RLengthNode lengthNode = RLengthNodeGen.create();
         @Child private WriteVariableNode writeVectorElement = WriteVariableNode.createAnonymous(VECTOR_ELEMENT, null, Mode.REGULAR);
-        @Child private WriteVariableNode writeIndex = WriteVariableNode.createAnonymous(INDEX_NAME, null, Mode.REGULAR);
-        @Child private RNode indexedLoadNode = createIndexedLoad();
-
-        public abstract Object[] execute(VirtualFrame frame, Object vector, RFunction function, RArgsValuesAndNames additionalArguments);
+        @Child private ExtractVectorNode extractElementNode = ExtractVectorNodeGen.create(ElementAccessMode.SUBSCRIPT, false);
+        @Child private RCallNode callNode = createCallNode();
 
-        private Object[] lApplyInternal(VirtualFrame frame, Object vector, RFunction function, RCallNode callNode) {
+        public abstract Object[] execute(VirtualFrame frame, Object vector, RFunction function);
 
-            int length = lengthNode.executeInt(frame, vector);
+        @Specialization
+        protected Object[] cachedLApply(VirtualFrame frame, Object vector, RFunction function) {
+            // TODO: R switches to double if x.getLength() is greater than 2^31-1
+            int length = lengthNode.executeInteger(frame, vector);
             Object[] result = new Object[length];
             for (int i = 1; i <= length; i++) {
-                writeIndex.execute(frame, i);
-                writeVectorElement.execute(frame, indexedLoadNode.execute(frame));
+                writeVectorElement.execute(frame, extractElementNode.apply(frame, vector, new Object[]{i}, RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_TRUE));
                 result[i - 1] = callNode.execute(frame, function);
             }
             return result;
         }
 
-        @SuppressWarnings("unused")
-        @Specialization(limit = "5", guards = {"function.getTarget() == cachedTarget", "additionalArguments.getSignature() == cachedSignature"})
-        protected Object[] cachedLApply(VirtualFrame frame, Object vector, RFunction function, RArgsValuesAndNames additionalArguments, //
-                        @Cached("function.getTarget()") RootCallTarget cachedTarget, //
-                        @Cached("additionalArguments.getSignature()") ArgumentsSignature cachedSignature, //
-                        @Cached("createCallNode(additionalArguments)") RCallNode callNode) {
-            return lApplyInternal(frame, vector, function, callNode);
-        }
-
-        @Specialization(contains = "cachedLApply")
-        protected Object[] genericLApply(VirtualFrame frame, Object vector, RFunction function, RArgsValuesAndNames additionalArguments) {
-
-            // TODO: implement more efficiently (how much does it matter considering that there is
-            // cached version?); previous comment here implied that having RCallNode executing with
-            // an evaluated RArgsValuesAndNames would help
-            return lApplyInternal(frame, vector, function, createCallNode(additionalArguments));
-        }
-
-        private static RNode createIndexedLoad() {
-            RCodeBuilder<RSyntaxNode> builder = RContext.getASTBuilder();
-            RSyntaxNode receiver = builder.lookup(RSyntaxNode.INTERNAL, "X", false);
-            RSyntaxNode index = builder.lookup(RSyntaxNode.INTERNAL, INDEX_NAME, false);
-            RSyntaxNode access = builder.lookup(RSyntaxNode.INTERNAL, "[[", true);
-            return builder.call(RSyntaxNode.INTERNAL, access, receiver, index).asRNode();
-        }
-
         /**
          * Creates the {@link RCallNode} for this target and {@code varArgs}.
-         *
-         * @param additionalArguments may be {@link RMissing#instance} to indicate empty "..."!
          */
-        @TruffleBoundary
-        protected RCallNode createCallNode(RArgsValuesAndNames additionalArguments) {
-            /* TODO: R switches to double if x.getLength() is greater than 2^31-1 */
-
-            ReadVariableNode readVector = ReadVariableNode.create(VECTOR_ELEMENT);
-
-            // The remaining parameters are passed from {@code ...}. The call node will take
-            // care of matching.
-            RSyntaxNode[] args;
-            String[] names;
-            if (additionalArguments.isEmpty()) {    // == null || (varArgs.length() == 1 &&
-                // varArgs.getValue(0)
-                // == RMissing.instance)) {
-                args = new RSyntaxNode[]{readVector};
-                names = new String[]{null};
-            } else {
-                // Insert expressions found inside "..." as arguments
-                args = new RSyntaxNode[additionalArguments.getLength() + 1];
-                args[0] = readVector;
-                Object[] varArgsValues = additionalArguments.getArguments();
-                for (int i = 0; i < additionalArguments.getLength(); i++) {
-                    args[i + 1] = (RSyntaxNode) wrapVarArgValue(varArgsValues[i], i);
-
-                }
-                names = new String[additionalArguments.getLength() + 1];
-                names[0] = null;
-                for (int i = 0; i < additionalArguments.getLength(); i++) {
-                    String name = additionalArguments.getSignature().getName(i);
-                    if (name != null && !name.isEmpty()) {
-                        // change "" to null
-                        names[i + 1] = name;
-                    }
-                }
-            }
-            ArgumentsSignature argsSig = ArgumentsSignature.get(names);
-            // Errors can be thrown from the modified call so a SourceSection is required
-            return RCallNode.createCall(createCallSourceSection(), null, argsSig, args);
+        protected RCallNode createCallNode() {
+            CompilerAsserts.neverPartOfCompilation();
+
+            ReadVariableNode readVector = ReadVariableNode.createSilent(VECTOR_ELEMENT, RType.Any);
+            ReadVariableNode readArgs = ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any);
+
+            return RCallNode.createCall(createCallSourceSection(), null, ArgumentsSignature.get(null, "..."), readVector, readArgs);
         }
     }
 
     static SourceSection createCallSourceSection() {
         return CALL_SOURCE.createSection("", 0, CALL_SOURCE.getLength());
     }
-
-    private static RNode wrapVarArgValue(Object varArgValue, int varArgIndex) {
-        if (varArgValue instanceof RPromise) {
-            return PromiseNode.createVarArg(varArgIndex);
-        } else {
-            return ConstantNode.create(varArgValue);
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
index 24c6e65e3f655e2e3378c8b03387308bae5c2af4..0211b1e052040aa33a31e1f497a07d163310034a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
@@ -33,25 +33,15 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.closures.RClosures;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-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.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
@@ -116,22 +106,22 @@ public abstract class Match extends RBuiltinNode {
         return RDataFactory.createIntVector(data, naCheck.neverSeenNA());
     }
 
-    @Specialization
-    protected Object match(RFactor x, RFactor table, RAbstractIntVector nomatchObj, Object incomparables) {
-        naCheck.enable(x.getVector());
-        naCheck.enable(table.getVector());
+    @Specialization(guards = {"isFactor(x)", "isFactor(table)"})
+    protected Object matchFactor(RAbstractIntVector x, RAbstractIntVector table, RAbstractIntVector nomatchObj, Object incomparables) {
+        naCheck.enable(x);
+        naCheck.enable(table);
         return matchRecursive(RClosures.createFactorToVector(x, true, attrProfiles), RClosures.createFactorToVector(table, true, attrProfiles), nomatchObj, incomparables);
     }
 
-    @Specialization
-    protected Object match(RFactor x, RAbstractVector table, RAbstractIntVector nomatchObj, Object incomparables) {
-        naCheck.enable(x.getVector());
+    @Specialization(guards = {"isFactor(x)", "!isFactor(table)"})
+    protected Object matchFactor(RAbstractIntVector x, RAbstractVector table, RAbstractIntVector nomatchObj, Object incomparables) {
+        naCheck.enable(x);
         return matchRecursive(RClosures.createFactorToVector(x, true, attrProfiles), table, nomatchObj, incomparables);
     }
 
-    @Specialization
-    protected Object match(RAbstractVector x, RFactor table, RAbstractIntVector nomatchObj, Object incomparables) {
-        naCheck.enable(table.getVector());
+    @Specialization(guards = {"!isFactor(x)", "isFactor(table)"})
+    protected Object matchFactor(RAbstractVector x, RAbstractIntVector table, RAbstractIntVector nomatchObj, Object incomparables) {
+        naCheck.enable(table);
         return matchRecursive(x, RClosures.createFactorToVector(table, true, attrProfiles), nomatchObj, incomparables);
     }
 
@@ -514,6 +504,12 @@ public abstract class Match extends RBuiltinNode {
         return table.getElementClass() == String.class;
     }
 
+    @Child private InheritsCheckNode factorInheritsCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
+
+    protected boolean isFactor(Object o) {
+        return factorInheritsCheck.execute(o);
+    }
+
     private static int[] initResult(int length, int nomatch) {
         int[] result = new int[length];
         Arrays.fill(result, nomatch);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
index d22d7cc5de0037762f2fc70e726cb5d80155aa12..a90acdd3f05996d26b537da99104d3c44df84fc9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
@@ -39,8 +39,8 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.MatchFunNodeGen.MatchFunInternalNodeGen;
+import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.nodes.function.signature.GetCallerFrameNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java
index 61b1c873ab9292923225d81a7b0c6ae5e159ff7d..6089d1efca02f36a976f96ecce2eb05cbeea04dd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
@@ -36,7 +37,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
-@RBuiltin(name = "mean", kind = INTERNAL, parameterNames = {"x"})
+@RBuiltin(name = "mean", kind = INTERNAL, parameterNames = {"x"}, dispatch = RDispatch.INTERNAL_GENERIC)
 public abstract class Mean extends RBuiltinNode {
 
     private final BranchProfile emptyProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
index 2fd7dcd2d573eb429624b06442c8aed04d77a155..9fb021ee9d1d4a05c1ecb5baefbe32bcec831ad8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
@@ -29,6 +29,7 @@ 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.RBuiltinNode;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.ConversionFailedException;
@@ -38,7 +39,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -49,6 +49,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 public abstract class NChar extends RBuiltinNode {
 
     @Child private CastStringNode convertString;
+    @Child private InheritsCheckNode factorInheritsCheck;
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
@@ -127,9 +128,15 @@ public abstract class NChar extends RBuiltinNode {
     @Fallback
     protected RIntVector nchar(Object obj, Object type, Object allowNA, Object keepNA) {
         controlVisibility();
-        if (obj instanceof RFactor) {
+        if (factorInheritsCheck == null) {
+            CompilerDirectives.transferToInterpreter();
+            factorInheritsCheck = insert(new InheritsCheckNode(RRuntime.CLASS_FACTOR));
+        }
+
+        if (factorInheritsCheck.execute(obj)) {
             throw RError.error(this, RError.Message.REQUIRES_CHAR_VECTOR, "nchar");
         }
+
         if (obj instanceof RAbstractVector) {
             RAbstractVector vector = (RAbstractVector) obj;
             int len = vector.getLength();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index db5b77ce172a33e066dfc815d4472fddcce728bc..5df55c8e7ecb29b4a63f9581d03e7f4c6be9f8e2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -236,9 +236,17 @@ public abstract class Parse extends RBuiltinNode {
     private static void addAttributes(RExpression exprs, Source source, REnvironment srcFile) {
         Object[] srcrefData = new Object[exprs.getLength()];
         for (int i = 0; i < srcrefData.length; i++) {
-            RBaseNode node = ((RLanguage) exprs.getDataAt(i)).getRep();
-            SourceSection ss = node.asRSyntaxNode().getSourceSection();
-            srcrefData[i] = RSrcref.createLloc(ss, srcFile);
+            Object data = exprs.getDataAt(i);
+            if (data instanceof RLanguage) {
+                RBaseNode node = ((RLanguage) data).getRep();
+                SourceSection ss = node.asRSyntaxNode().getSourceSection();
+                srcrefData[i] = RSrcref.createLloc(ss, srcFile);
+            } else if (data == RNull.instance) {
+                srcrefData[i] = data;
+            } else {
+                throw RInternalError.unimplemented();
+            }
+
         }
         exprs.setAttr("srcref", RDataFactory.createList(srcrefData));
         int[] wholeSrcrefData = new int[8];
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
index d4610cda7c2bb5c9a4ec460a82b86bb885ce2262..80c47dc37cab89037064f954c443331d0d1b3293 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
@@ -37,56 +37,20 @@ import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrettyPrinterSingleListElementNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrettyPrinterSingleVectorElementNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrintDimNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrintVector2DimNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrintVectorMultiDimNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.*;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+import com.oracle.truffle.r.nodes.helpers.RFactorNodes;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.conn.SocketConnections;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RAttributes;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-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.RPairList;
-import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RString;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.closures.RClosures;
 import com.oracle.truffle.r.runtime.data.closures.RFactorToStringVectorClosure;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
@@ -1185,7 +1149,7 @@ public abstract class PrettyPrinterNode extends RNode {
         }
 
         @TruffleBoundary
-        @Specialization
+        @Specialization(guards = "!isFactor(operand)")
         protected String prettyPrintListElement(RAbstractVector operand, Object listElementName, byte quote, byte right) {
             return prettyPrintSingleElement(operand, listElementName, quote, right);
         }
@@ -1220,10 +1184,22 @@ public abstract class PrettyPrinterNode extends RNode {
             return prettyPrintSingleElement(operand, listElementName, quote, right);
         }
 
+        @Child InheritsCheckNode factorInheritsCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
+        @Child RFactorNodes.GetLevels getFactorLevels;
+
+        protected boolean isFactor(Object o) {
+            return this.factorInheritsCheck.execute(o);
+        }
+
         // TODO: this should be handled by an S3 function
-        @Specialization
-        protected String prettyPrintListElement(RFactor operand, Object listElementName, byte quote, byte right) {
-            RVector vec = operand.getLevels(attrProfiles);
+        @Specialization(guards = "isFactor(factor)")
+        protected String prettyPrintListElement(RAbstractIntVector factor, Object listElementName, byte quote, byte right) {
+            if (getFactorLevels == null) {
+                CompilerDirectives.transferToInterpreter();
+                getFactorLevels = insert(new RFactorNodes.GetLevels());
+            }
+
+            RVector vec = getFactorLevels.execute(factor);
             String[] strings;
             if (vec == null) {
                 strings = new String[0];
@@ -1234,11 +1210,11 @@ public abstract class PrettyPrinterNode extends RNode {
                     strings[i] = (String) castStringNode.executeString(vec.getDataAtAsObject(i));
                 }
             }
-            return formatLevelStrings(operand, listElementName, right, vec, strings);
+            return formatLevelStrings(factor, listElementName, right, vec, strings);
         }
 
         @TruffleBoundary
-        private String formatLevelStrings(RFactor operand, Object listElementName, byte right, RVector vec, String[] strings) {
+        private String formatLevelStrings(RAbstractIntVector operand, Object listElementName, byte right, RVector vec, String[] strings) {
             StringBuilder sb = new StringBuilder(prettyPrintSingleElement(RClosures.createFactorToVector(operand, true, attrProfiles), listElementName, RRuntime.LOGICAL_FALSE, right));
             sb.append("\nLevels:");
             if (vec != null) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
index eb19a426e9033c2025c18d6f82d1476f51c1babf..cc1f2ce326cfc7298fdbe351ce68f1af6115875c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
@@ -26,22 +26,16 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 
 import java.util.Arrays;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -66,27 +60,17 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
  * </ul>
  * </ol>
  */
-@RBuiltin(name = "rep", kind = PRIMITIVE, parameterNames = {"x", "times", "length.out", "each"})
+@RBuiltin(name = "rep", kind = PRIMITIVE, parameterNames = {"x", "times", "length.out", "each"}, dispatch = RDispatch.INTERNAL_GENERIC)
 public abstract class Repeat extends RBuiltinNode {
 
     protected abstract Object execute(RAbstractVector x, RAbstractIntVector times, int lengthOut, int each);
 
-    @Child private Repeat repeatRecursive;
-
     private final ConditionProfile lengthOutOrTimes = ConditionProfile.createBinaryProfile();
     private final BranchProfile errorBranch = BranchProfile.create();
     private final ConditionProfile oneTimeGiven = ConditionProfile.createBinaryProfile();
     private final ConditionProfile replicateOnce = ConditionProfile.createBinaryProfile();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-    private Object repeatRecursive(RAbstractVector x, RAbstractIntVector times, int lengthOut, int each) {
-        if (repeatRecursive == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            repeatRecursive = insert(RepeatNodeGen.create(null));
-        }
-        return repeatRecursive.execute(x, times, lengthOut, each);
-    }
-
     @Override
     public Object[] getDefaultParameterValues() {
         return new Object[]{RMissing.instance, 1, RRuntime.INT_NA, 1};
@@ -174,13 +158,6 @@ public abstract class Repeat extends RBuiltinNode {
         }
     }
 
-    @Specialization
-    protected RAbstractContainer rep(RFactor x, RAbstractIntVector times, int lengthOut, int each) {
-        RVector vec = (RVector) repeatRecursive(x.getVector(), times, lengthOut, each);
-        vec.setAttr(RRuntime.LEVELS_ATTR_KEY, x.getLevels(attrProfiles));
-        return RVector.setVectorClassAttr(vec, x.getClassAttr(attrProfiles), null);
-    }
-
     /**
      * Prepare the input vector by replicating its elements.
      */
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java
index e1d45d77d9521b86cf4245a8d4143d3f6914d04d..0edc9d0761c24c95455103fda2bc949061b6fb2c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java
@@ -26,27 +26,18 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
+import com.oracle.truffle.r.nodes.helpers.RFactorNodes;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-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.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 /**
- * The {@code split} internal.
+ * The {@code split} internal. Internal version of 'split' is invoked from 'split.default' function
+ * implemented in R, which makes sure that the second argument is always a R factor.
  *
  * TODO Can we find a way to efficiently write the specializations as generics? The code is
  * identical except for the argument type.
@@ -54,18 +45,23 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 @RBuiltin(name = "split", kind = INTERNAL, parameterNames = {"x", "f"})
 public abstract class Split extends RBuiltinNode {
 
-    @Child private CastStringNode castString;
+    @Child private RFactorNodes.GetLevels getLevelNode = new RFactorNodes.GetLevels();
 
     private final ConditionProfile noStringLevels = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
     private static final int INITIAL_SIZE = 5;
     private static final int SCALE_FACTOR = 2;
 
+    public static class SplitTemplate {
+        private int[] collectResulSize;
+        private int nLevels;
+    }
+
     @Specialization
-    protected RList split(RAbstractIntVector x, RFactor f) {
-        int[] factor = f.getVector().getDataWithoutCopying();
-        final int nLevels = f.getNLevels(attrProfiles);
+    protected RList split(RAbstractIntVector x, RAbstractIntVector f) {
+        int[] factor = f.materialize().getDataWithoutCopying();
+        RStringVector names = getLevelNode.execute(f);
+        final int nLevels = getNLevels(names);
 
         // initialise result arrays
         int[][] collectResults = new int[nLevels][];
@@ -91,13 +87,14 @@ public abstract class Split extends RBuiltinNode {
             results[i] = RDataFactory.createIntVector(Arrays.copyOfRange(collectResults[i], 0, collectResultSize[i]), x.isComplete());
         }
 
-        return RDataFactory.createList(results, makeNames(f));
+        return RDataFactory.createList(results, names);
     }
 
     @Specialization
-    protected RList split(RAbstractDoubleVector x, RFactor f) {
-        int[] factor = f.getVector().getDataWithoutCopying();
-        final int nLevels = f.getNLevels(attrProfiles);
+    protected RList split(RAbstractDoubleVector x, RAbstractIntVector f) {
+        int[] factor = f.materialize().getDataWithoutCopying();
+        RStringVector names = getLevelNode.execute(f);
+        final int nLevels = getNLevels(names);
 
         // initialise result arrays
         double[][] collectResults = new double[nLevels][];
@@ -123,13 +120,14 @@ public abstract class Split extends RBuiltinNode {
             results[i] = RDataFactory.createDoubleVector(Arrays.copyOfRange(collectResults[i], 0, collectResultSize[i]), RDataFactory.COMPLETE_VECTOR);
         }
 
-        return RDataFactory.createList(results, makeNames(f));
+        return RDataFactory.createList(results, names);
     }
 
     @Specialization
-    protected RList split(RAbstractStringVector x, RFactor f) {
-        int[] factor = f.getVector().getDataWithoutCopying();
-        final int nLevels = f.getNLevels(attrProfiles);
+    protected RList split(RAbstractStringVector x, RAbstractIntVector f) {
+        int[] factor = f.materialize().getDataWithoutCopying();
+        RStringVector names = getLevelNode.execute(f);
+        final int nLevels = getNLevels(names);
 
         // initialise result arrays
         String[][] collectResults = new String[nLevels][];
@@ -155,13 +153,14 @@ public abstract class Split extends RBuiltinNode {
             results[i] = RDataFactory.createStringVector(Arrays.copyOfRange(collectResults[i], 0, collectResultSize[i]), RDataFactory.COMPLETE_VECTOR);
         }
 
-        return RDataFactory.createList(results, makeNames(f));
+        return RDataFactory.createList(results, names);
     }
 
     @Specialization
-    protected RList split(RAbstractLogicalVector x, RFactor f) {
-        int[] factor = f.getVector().getDataWithoutCopying();
-        final int nLevels = f.getNLevels(attrProfiles);
+    protected RList split(RAbstractLogicalVector x, RAbstractIntVector f) {
+        int[] factor = f.materialize().getDataWithoutCopying();
+        RStringVector names = getLevelNode.execute(f);
+        final int nLevels = getNLevels(names);
 
         // initialise result arrays
         byte[][] collectResults = new byte[nLevels][];
@@ -187,20 +186,10 @@ public abstract class Split extends RBuiltinNode {
             results[i] = RDataFactory.createLogicalVector(Arrays.copyOfRange(collectResults[i], 0, collectResultSize[i]), x.isComplete());
         }
 
-        return RDataFactory.createList(results, makeNames(f));
+        return RDataFactory.createList(results, names);
     }
 
-    private RStringVector makeNames(RFactor f) {
-        RVector levels = f.getLevels(attrProfiles);
-        if (noStringLevels.profile(!(levels instanceof RStringVector))) {
-            if (castString == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castString = insert(CastStringNodeGen.create(false, false, false, false));
-            }
-            RStringVector slevels = (RStringVector) castString.executeString(f.getLevels(attrProfiles));
-            return RDataFactory.createStringVector(slevels.getDataWithoutCopying(), RDataFactory.COMPLETE_VECTOR);
-        } else {
-            return RDataFactory.createStringVector(((RStringVector) levels).getDataCopy(), RDataFactory.COMPLETE_VECTOR);
-        }
+    private int getNLevels(RStringVector levels) {
+        return levels != null ? levels.getLength() : 0;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
index e10fb7d5ba8a50dc897dbc49c3494301dfbbbfa5..829cc8dcc93baee18b08e6ff57b7def021e0eda8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
@@ -22,7 +22,6 @@ import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
@@ -55,11 +54,6 @@ public abstract class Tabulate extends RBuiltinNode {
         return RDataFactory.createIntVector(ans, RDataFactory.COMPLETE_VECTOR);
     }
 
-    @Specialization
-    protected RIntVector tabulate(RFactor bin, int nBins) {
-        return tabulate(bin.getVector(), nBins);
-    }
-
     @SuppressWarnings("unused")
     @Fallback
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
index 22fc1a59678a9cd65c782a19ceba5eaec0d77c2b..92572424d17d380f3828b5d4a8bda9ffa9e41b49 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
@@ -20,7 +20,6 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RVector;
@@ -48,19 +47,6 @@ public abstract class UnClass extends RBuiltinNode {
         return arg;
     }
 
-    @Specialization
-    @TruffleBoundary
-    protected Object unClass(RFactor arg) {
-        controlVisibility();
-        RFactor resultFactor = arg;
-        if (!resultFactor.isTemporary()) {
-            resultFactor = resultFactor.copy();
-            assert resultFactor.isTemporary();
-            resultFactor.incRefCount();
-        }
-        return RVector.setVectorClassAttr(resultFactor.getVector(), null, arg);
-    }
-
     @Specialization
     protected Object unClass(RLanguage arg) {
         controlVisibility();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java
index 5f954d44f38633e733a4005c6b6b89a205957053..7e7efef62fbe2f3ec3e406d0a44acbf323668797 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java
@@ -35,27 +35,8 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 // Implements default S3 method
 @RBuiltin(name = "unique", kind = INTERNAL, parameterNames = {"x", "incomparables", "fromLast", "nmax", "..."})
@@ -86,11 +67,6 @@ public abstract class Unique extends RBuiltinNode {
         return vec;
     }
 
-    @Specialization
-    protected RAbstractContainer doUnique(VirtualFrame frame, RFactor factor, byte incomparables, byte fromLast, Object nmax, RArgsValuesAndNames vararg) {
-        return uniqueRecursive(frame, factor.getVector(), incomparables, fromLast, nmax, vararg);
-    }
-
     @SuppressWarnings("unused")
     @Specialization
     protected RStringVector doUnique(RAbstractStringVector vec, byte incomparables, byte fromLast, Object nmax, RArgsValuesAndNames vararg) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
index 16426dd276fd6dcdbc7d0e2dc9e5a5ee15ca25a7..0d3d387ae3876196022c19cb9b30c9986a3764fa 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
@@ -14,24 +14,21 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RInvisibleBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "levels<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "levels<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "value"}, dispatch = RDispatch.INTERNAL_GENERIC)
 public abstract class UpdateLevels extends RInvisibleBuiltinNode {
 
     @Child private CastToVectorNode castVector;
-    @Child private CastStringNode castString;
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
@@ -43,14 +40,6 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode {
         return (RAbstractVector) castVector.execute(value);
     }
 
-    private Object castString(Object operand) {
-        if (castString == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(false, true, false, false));
-        }
-        return castString.execute(operand);
-    }
-
     @Specialization
     protected RAbstractVector updateLevels(RAbstractVector vector, @SuppressWarnings("unused") RNull levels) {
         controlVisibility();
@@ -67,20 +56,6 @@ public abstract class UpdateLevels extends RInvisibleBuiltinNode {
         return v;
     }
 
-    @Specialization
-    protected RFactor updateLevels(RFactor factor, @SuppressWarnings("unused") RNull levels) {
-        controlVisibility();
-        factor.getVector().removeAttr(attrProfiles, RRuntime.LEVELS_ATTR_KEY);
-        return factor;
-    }
-
-    @Specialization(guards = "levelsNotNull(levels)")
-    protected RFactor updateLevels(RFactor factor, Object levels) {
-        controlVisibility();
-        factor.getVector().setAttr(RRuntime.LEVELS_ATTR_KEY, castString(castVector(levels)));
-        return factor;
-    }
-
     protected boolean levelsNotNull(Object levels) {
         return levels != RNull.instance;
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
index c8d3bee04b3018e0ad0081c02e1acf441bacd3b0..f7af4bdaca193ced16ae3d395051cf139ed7f589 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
@@ -43,12 +43,10 @@ import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -132,14 +130,13 @@ public abstract class VApply extends RBuiltinNode {
 
     @Specialization
     protected Object vapply(VirtualFrame frame, RAbstractVector vec, RFunction fun, Object funValue, byte useNames) {
-        RArgsValuesAndNames optionalArgs = (RArgsValuesAndNames) RArguments.getArgument(frame, 3);
-        RVector result = delegateToLapply(frame, vec, fun, funValue, useNames, optionalArgs);
+        RVector result = delegateToLapply(frame, vec, fun, funValue, useNames);
         // set here else it gets overridden by the iterator evaluation
         controlVisibility();
         return result;
     }
 
-    private RVector delegateToLapply(VirtualFrame frame, RAbstractVector vec, RFunction fun, Object funValueArg, byte useNames, RArgsValuesAndNames optionalArgs) {
+    private RVector delegateToLapply(VirtualFrame frame, RAbstractVector vec, RFunction fun, Object funValueArg, byte useNames) {
         /*
          * The implementation is complicated by the existence of scalar length 1 vectors (e.g.
          * Integer) and concrete length 1 vectors (e.g. RIntVector), as either form can occur in
@@ -155,7 +152,7 @@ public abstract class VApply extends RBuiltinNode {
         int funValueVecLen = funValueVec.getLength();
 
         RVector vecMat = vec.materialize();
-        Object[] applyResult = doApply.execute(frame, vecMat, fun, optionalArgs);
+        Object[] applyResult = doApply.execute(frame, vecMat, fun);
 
         RVector result = null;
         boolean applyResultZeroLength = applyResult.length == 0;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FactorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FactorPrinter.java
index b92ab79ade065e95f2a6724b8ec6f46f0190c861..595240c6942ddc969e97d1170dca5ceced16a96d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FactorPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FactorPrinter.java
@@ -29,9 +29,10 @@ import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.closures.RClosures;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-final class FactorPrinter extends AbstractValuePrinter<RFactor> {
+final class FactorPrinter extends AbstractValuePrinter<RAbstractIntVector> {
 
     static final FactorPrinter INSTANCE = new FactorPrinter();
 
@@ -42,28 +43,30 @@ final class FactorPrinter extends AbstractValuePrinter<RFactor> {
     private static RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
 
     @Override
-    protected void printValue(RFactor operand, PrintContext printCtx) throws IOException {
-        // TODO: this should be handled by an S3 function
-        RVector vec = operand.getLevels(dummyAttrProfiles);
+    protected void printValue(RAbstractIntVector operand, PrintContext printCtx) throws IOException {
+        // TODO: this should be handled by an S3 function. Should it? For example, in C code for
+        // split, there is direct call to getAttrib. This should be refactored to use
+        // AttributeAccess node or even Factor.GetLevels node. The same holds for the access
+        RVector levels = RFactor.getLevels(operand);
         String[] strings;
-        if (vec == null) {
+        if (levels == null) {
             strings = new String[0];
         } else {
-            strings = new String[vec.getLength()];
-            for (int i = 0; i < vec.getLength(); i++) {
-                strings[i] = printCtx.printerNode().castString(vec.getDataAtAsObject(i));
+            strings = new String[levels.getLength()];
+            for (int i = 0; i < levels.getLength(); i++) {
+                strings[i] = printCtx.printerNode().castString(levels.getDataAtAsObject(i));
             }
         }
 
-        RAbstractVector v = RClosures.createFactorToVector(operand, true, dummyAttrProfiles);
+        RAbstractVector v = RClosures.createFactorToVector(operand, true, levels);
         PrintContext vectorPrintCtx = printCtx.cloneContext();
         vectorPrintCtx.parameters().setQuote(false);
         ValuePrinters.INSTANCE.println(v, vectorPrintCtx);
 
         final PrintWriter out = printCtx.output();
         out.print("Levels:");
-        if (vec != null) {
-            for (int i = 0; i < vec.getLength(); i++) {
+        if (levels != null) {
+            for (int i = 0; i < levels.getLength(); i++) {
                 out.print(" ");
                 out.print(strings[i]);
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java
index 8e4fb77736917b561ad1bf6339037598e0993758..fc0f02ef92c2aeac9cacac2dca11e249d2b8c94b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinters.java
@@ -23,15 +23,8 @@
 package com.oracle.truffle.r.nodes.builtin.base.printer;
 
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -59,7 +52,6 @@ final class ValuePrinters implements ValuePrinter<Object> {
         printers.put(RExternalPtr.class, ExternalPtrPrinter.INSTANCE);
         printers.put(RS4Object.class, S4ObjectPrinter.INSTANCE);
         printers.put(RPairList.class, PairListPrinter.INSTANCE);
-        printers.put(RFactor.class, FactorPrinter.INSTANCE);
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})
@@ -74,7 +66,9 @@ final class ValuePrinters implements ValuePrinter<Object> {
             Object x = printCtx.printerNode().boxPrimitive(v);
             ValuePrinter printer = printers.get(x.getClass());
             if (printer == null) {
-                if (x instanceof RAbstractStringVector) {
+                if (x instanceof RAbstractIntVector && ((RAttributable) x).hasClass(RRuntime.CLASS_FACTOR)) {
+                    printer = FactorPrinter.INSTANCE;
+                } else if (x instanceof RAbstractStringVector) {
                     printer = StringVectorPrinter.INSTANCE;
                 } else if (x instanceof RAbstractDoubleVector) {
                     printer = DoubleVectorPrinter.INSTANCE;
@@ -91,7 +85,7 @@ final class ValuePrinters implements ValuePrinter<Object> {
                 } else if (x instanceof REnvironment) {
                     printer = EnvironmentPrinter.INSTANCE;
                 } else {
-                    RInternalError.shouldNotReachHere();
+                    RInternalError.shouldNotReachHere("unexpected type: " + (x == null ? "null" : x.getClass()));
                 }
             }
             printer.print(x, printCtx);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRCompile.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRCompile.java
deleted file mode 100644
index 579901bfbfb5497c89a84305d464388150f78056..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRCompile.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2013, 2016, 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.fastr;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-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.RFunction;
-import com.oracle.truffle.r.runtime.data.RMissing;
-
-@RBuiltin(name = ".fastr.compile", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "background"})
-public abstract class FastRCompile extends RBuiltinNode {
-
-    private static final class Compiler {
-        private final Class<?> optimizedCallTarget;
-        private final Class<?> graalTruffleRuntime;
-        private final Method compileMethod;
-
-        private Compiler() {
-            try {
-                optimizedCallTarget = Class.forName("com.oracle.graal.truffle.OptimizedCallTarget", false, Truffle.getRuntime().getClass().getClassLoader());
-                graalTruffleRuntime = Class.forName("com.oracle.graal.truffle.GraalTruffleRuntime", false, Truffle.getRuntime().getClass().getClassLoader());
-                compileMethod = graalTruffleRuntime.getDeclaredMethod("compile", optimizedCallTarget, boolean.class);
-            } catch (ClassNotFoundException | IllegalArgumentException | NoSuchMethodException | SecurityException e) {
-                throw Utils.fail("fastr.compile: failed to find 'compile' method");
-            }
-        }
-
-        static Compiler getCompiler() {
-            if (System.getProperty("fastr.truffle.compile", "true").equals("true") && Truffle.getRuntime().getName().contains("Graal")) {
-                return new Compiler();
-            } else {
-                return null;
-            }
-        }
-
-        boolean compile(CallTarget callTarget, boolean background) throws InvocationTargetException, IllegalAccessException {
-            if (optimizedCallTarget.isInstance(callTarget)) {
-                compileMethod.invoke(Truffle.getRuntime(), callTarget, background);
-                return true;
-            } else {
-                return false;
-            }
-        }
-    }
-
-    private static final Compiler compiler = Compiler.getCompiler();
-
-    @Override
-    public Object[] getDefaultParameterValues() {
-        return new Object[]{RMissing.instance, RRuntime.LOGICAL_FALSE};
-    }
-
-    @Specialization
-    protected byte compileFunction(RFunction function, byte background) {
-        controlVisibility();
-        if (compiler != null) {
-            try {
-                if (compiler.compile(function.getTarget(), background == RRuntime.LOGICAL_TRUE)) {
-                    return RRuntime.LOGICAL_TRUE;
-                }
-            } catch (InvocationTargetException | IllegalAccessException e) {
-                throw RError.error(this, RError.Message.GENERIC, e.toString());
-            }
-        } else {
-            throw RError.error(this, RError.Message.GENERIC, "fastr.compile not supported in this environment");
-        }
-        return RRuntime.LOGICAL_FALSE;
-    }
-
-    @SuppressWarnings("unused")
-    @Fallback
-    protected Object fallback(Object a1, Object a2) {
-        throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
index 086bc472e7a6ecddd14f3786b18c863f5d82575d..0cb3089195d7c58a088a2b89343f0d43a7fa116a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
@@ -55,9 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public class FastRContext {
 
-    // TODO remove aliases once all clients are converted
-
-    @RBuiltin(aliases = "fastr.context.create", name = ".fastr.context.create", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"args", "kind"})
+    @RBuiltin(name = ".fastr.context.create", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"args", "kind"})
     public abstract static class Create extends RBuiltinNode {
 
         @Override
@@ -85,7 +83,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.context.get", name = ".fastr.context.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {})
+    @RBuiltin(name = ".fastr.context.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {})
     public abstract static class Get extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -117,7 +115,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.context.spawn", name = ".fastr.context.spawn", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts", "exprs"})
+    @RBuiltin(name = ".fastr.context.spawn", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts", "exprs"})
     public abstract static class Spawn extends RInvisibleBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -141,7 +139,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.context.join", name = ".fastr.context.join", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts"})
+    @RBuiltin(name = ".fastr.context.join", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts"})
     public abstract static class Join extends RInvisibleBuiltinNode {
         @Specialization
         protected RNull eval(RAbstractIntVector contexts) {
@@ -176,7 +174,7 @@ public class FastRContext {
      * It may also have an attribute "error" if the evaluation threw an exception, in which case the
      * result will be NA.
      */
-    @RBuiltin(aliases = "fastr.context.eval", name = ".fastr.context.eval", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts", "exprs", "par"})
+    @RBuiltin(name = ".fastr.context.eval", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts", "exprs", "par"})
     public abstract static class Eval extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -252,7 +250,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.create", name = ".fastr.channel.create", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"key"})
+    @RBuiltin(name = ".fastr.channel.create", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"key"})
     public abstract static class CreateChannel extends RBuiltinNode {
         @Specialization(guards = "key.getLength() == 1")
         @TruffleBoundary
@@ -267,7 +265,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.get", name = ".fastr.channel.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"key"})
+    @RBuiltin(name = ".fastr.channel.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"key"})
     public abstract static class GetChannel extends RBuiltinNode {
         @Specialization(guards = "key.getLength() == 1")
         @TruffleBoundary
@@ -282,7 +280,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.close", name = ".fastr.channel.close", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
+    @RBuiltin(name = ".fastr.channel.close", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
     public abstract static class CloseChannel extends RInvisibleBuiltinNode {
         @Specialization(guards = "id.getLength() == 1")
         @TruffleBoundary
@@ -298,7 +296,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.send", name = ".fastr.channel.send", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id", "data"})
+    @RBuiltin(name = ".fastr.channel.send", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id", "data"})
     public abstract static class ChannelSend extends RInvisibleBuiltinNode {
         @Specialization(guards = "id.getLength() == 1")
         @TruffleBoundary
@@ -314,7 +312,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.receive", name = ".fastr.channel.receive", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
+    @RBuiltin(name = ".fastr.channel.receive", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
     public abstract static class ChannelReceive extends RBuiltinNode {
         @Specialization(guards = "id.getLength() == 1")
         @TruffleBoundary
@@ -329,7 +327,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.poll", name = ".fastr.channel.poll", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
+    @RBuiltin(name = ".fastr.channel.poll", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
     public abstract static class ChannelPoll extends RBuiltinNode {
         @Specialization(guards = "id.getLength() == 1")
         @TruffleBoundary
@@ -344,7 +342,7 @@ public class FastRContext {
         }
     }
 
-    @RBuiltin(aliases = "fastr.channel.select", name = ".fastr.channel.select", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"ids"})
+    @RBuiltin(name = ".fastr.channel.select", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"ids"})
     public abstract static class ChannelSelect extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
index 34b9ab3141c55b3c626ea9f0f4e9730a52c8a580..cbfb84aa0fbfd4c31bff273cdec73584d94342c2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
@@ -147,10 +147,10 @@ public abstract class BrowserInteractNode extends RNode {
     }
 
     private static String getSrcinfo(RStringVector element) {
-        Object srcref = element.getAttribute(RRuntime.R_SRCREF);
+        Object srcref = element.getAttr(RRuntime.R_SRCREF);
         if (srcref != null) {
             RIntVector lloc = (RIntVector) srcref;
-            Object srcfile = lloc.getAttribute(RRuntime.R_SRCFILE);
+            Object srcfile = lloc.getAttr(RRuntime.R_SRCFILE);
             if (srcfile != null) {
                 REnvironment env = (REnvironment) srcfile;
                 return " at " + RRuntime.asString(env.get(RSrcref.SrcrefFields.filename.name())) + "#" + lloc.getDataAt(0);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
index d7383de899da867de3a815ae9a7c125849030626..1418ab39e90504bb45fd8867c7bec30ed9562d02 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java
@@ -170,7 +170,7 @@ public class TraceHandling {
             if (!disabled()) {
                 int depth = RArguments.getDepth(frame);
                 try {
-                    for (int i = 0; i < depth; i++) {
+                    for (int i = 0; i < depth - 1; i++) {
                         outputHandler.writeString(" ", false);
                     }
                     String callString = getCallSource(frame);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
index 8bd5d1b65a89c763abe6ee2c33fde7567445e887..da6c3c3242a5235a27cb6416132d5626e25bf949 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
@@ -44,7 +44,6 @@ import com.oracle.truffle.r.nodes.control.WhileNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
-import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
 import com.oracle.truffle.r.nodes.function.PostProcessArgumentsNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
@@ -53,15 +52,13 @@ import com.oracle.truffle.r.nodes.unary.GetNonSharedNodeGen;
 import com.oracle.truffle.r.parser.tools.EvaluatedArgumentsVisitor;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.FastPathFactory;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.REmpty;
+import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -133,11 +130,6 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
                         return new NextNode(source);
                 }
             } else if (args.size() == 1) {
-                // handle unary arithmetics, for the time being
-                RBuiltinDescriptor builtin = RContext.lookupBuiltinDescriptor(symbol);
-                if (builtin != null && builtin.getDispatch() == RDispatch.OPS_GROUP_GENERIC) {
-                    return GroupDispatchNode.create(symbol, source, ArgumentsSignature.empty(1), args.get(0).value);
-                }
                 switch (symbol) {
                     case "repeat":
                         return WhileNode.create(source, ConstantNode.create(RRuntime.LOGICAL_TRUE), args.get(0).value, true);
@@ -145,11 +137,6 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
                         return args.get(0).value;
                 }
             } else if (args.size() == 2) {
-                // handle binary arithmetics, for the time being
-                RBuiltinDescriptor builtin = RContext.lookupBuiltinDescriptor(symbol);
-                if (builtin != null && builtin.getDispatch() == RDispatch.OPS_GROUP_GENERIC) {
-                    return GroupDispatchNode.create(symbol, source, ArgumentsSignature.empty(2), args.get(0).value, args.get(1).value);
-                }
                 switch (symbol) {
                     case "while":
                         return WhileNode.create(source, args.get(0).value, args.get(1).value, false);
@@ -190,13 +177,6 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
                         arg -> (arg.value == null && arg.name == null) ? ConstantNode.create(arg.source == null ? RSyntaxNode.SOURCE_UNAVAILABLE : arg.source, REmpty.instance) : arg.value).toArray(
                                         RSyntaxNode[]::new);
 
-        if (lhs instanceof RSyntaxLookup) {
-            String symbol = ((RSyntaxLookup) lhs).getIdentifier();
-            RBuiltinDescriptor builtin = RContext.lookupBuiltinDescriptor(symbol);
-            if (builtin != null && builtin.getDispatch().isGroupGeneric()) {
-                return GroupDispatchNode.create(symbol, source, signature, nodes);
-            }
-        }
         return RCallNode.createCall(source, lhs.asRNode(), signature, nodes);
     }
 
@@ -457,8 +437,6 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> {
 
     @Override
     public RSyntaxNode constant(SourceSection source, Object value) {
-        assert value instanceof Byte || value instanceof Integer || value instanceof Double || value instanceof RComplex || value instanceof String || value instanceof RNull ||
-                        value instanceof REmpty || value instanceof RSymbol || value instanceof RAbstractVector : value.getClass();
         if (value instanceof String && !RRuntime.isNA((String) value)) {
             return ConstantNode.create(source, ((String) value).intern());
         } else {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
index bd7831a895ffe8d55b70a95654b9aed682441073..20e39ffd491a6b5d87a7874a30c066370e140772 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.nodes;
 
-import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
 import com.oracle.truffle.api.frame.VirtualFrame;
@@ -34,15 +33,11 @@ import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.ReadVariadicComponentNode;
 import com.oracle.truffle.r.nodes.access.variables.NamedRNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
-import com.oracle.truffle.r.nodes.function.CallArgumentsNode;
-import com.oracle.truffle.r.nodes.function.GroupDispatchNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.WrapArgumentBaseNode;
 import com.oracle.truffle.r.nodes.function.WrapArgumentNode;
-import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -67,6 +62,7 @@ public class RASTUtils {
      * .
      */
     public static <T extends RBaseNode> T cloneNode(T node) {
+        // TODO: use RASTBuilder here as well?
         return NodeUtil.cloneNode(node);
     }
 
@@ -126,6 +122,9 @@ public class RASTUtils {
             return value;
         } else if (argNode instanceof ReadVariableNode) {
             return RASTUtils.createRSymbol(argNode);
+        } else if (argNode instanceof VarArgNode) {
+            VarArgNode varArgNode = (VarArgNode) argNode;
+            return RDataFactory.createSymbolInterned(varArgNode.getIdentifier());
         } else {
             assert !(argNode instanceof VarArgNode);
             return RDataFactory.createLanguage((RNode) argNode);
@@ -224,9 +223,6 @@ public class RASTUtils {
             return RCallNode.createCall(sourceSection, (ReadVariableNode) fn, signature, arguments);
         } else if (fn instanceof NamedRNode) {
             return RCallNode.createCall(RSyntaxNode.SOURCE_UNAVAILABLE, (NamedRNode) fn, signature, arguments);
-        } else if (fn instanceof GroupDispatchNode) {
-            GroupDispatchNode gdcn = (GroupDispatchNode) fn;
-            return GroupDispatchNode.create(gdcn.getGenericName(), gdcn.getCallSrc(), signature, arguments);
         } else if (fn instanceof RFunction) {
             RFunction rfn = (RFunction) fn;
             return RCallNode.createCall(sourceSection, ConstantNode.create(rfn), signature, arguments);
@@ -240,55 +236,6 @@ public class RASTUtils {
         }
     }
 
-    /**
-     * Find the {@link CallArgumentsNode} that is the child of {@code node}. N.B. Does not copy.
-     */
-    public static Arguments<RSyntaxNode> findCallArguments(Node node) {
-        if (node instanceof RCallNode) {
-            return ((RCallNode) node).getArguments();
-        } else if (node instanceof GroupDispatchNode) {
-            return ((GroupDispatchNode) node).getArguments();
-        }
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    /**
-     * Returns the name (as an {@link RSymbol} of the function associated with an {@link RCallNode}
-     * or {@link GroupDispatchNode}.
-     */
-    public static Object findFunctionName(RBaseNode node) {
-        CompilerAsserts.neverPartOfCompilation(); // for string interning
-        RNode child = (RNode) unwrap(getFunctionNode(node));
-        if (child instanceof ConstantNode && ConstantNode.isFunction(child)) {
-            return ((ConstantNode) child).getValue();
-        } else if (child instanceof ReadVariableNode) {
-            String name = ((ReadVariableNode) child).getIdentifier();
-            assert name == name.intern();
-            return RDataFactory.createSymbol(name);
-        } else if (child instanceof GroupDispatchNode) {
-            GroupDispatchNode groupDispatchNode = (GroupDispatchNode) child;
-            String gname = groupDispatchNode.getGenericName();
-            return RDataFactory.createSymbolInterned(gname);
-        } else {
-            // TODO This should really fail in some way as (clearly) this is not a "name"
-            // some more complicated expression, just deparse it
-            return RDataFactory.createSymbolInterned(RDeparse.deparse(child));
-        }
-    }
-
-    /**
-     * Unifies {@link RCallNode} and {@link GroupDispatchNode} for accessing (likely) function name.
-     */
-    public static RNode getFunctionNode(Node node) {
-        if (node instanceof RCallNode) {
-            return ((RCallNode) node).getFunctionNode();
-        } else if (node instanceof GroupDispatchNode) {
-            return (RNode) node;
-        }
-        assert false;
-        return null;
-    }
-
     @TruffleBoundary
     /**
      * The heart of the {@code substitute} function, where we look up the value of {@code name} in
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
index b71392f90ed51fe0dd42d83c02dad648e03a5295..ae23ed865a3cd740638ecaa3feda691e0bcc2cd3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ConstantNode.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.VisibilityController;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RSymbol;
@@ -46,10 +45,6 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
         super(sourceSection);
     }
 
-    public static boolean isFunction(RNode node) {
-        return node instanceof ConstantObjectNode && ((ConstantObjectNode) node).value instanceof RFunction;
-    }
-
     public static boolean isMissing(RNode node) {
         return node instanceof ConstantObjectNode && ((ConstantObjectNode) node).value == RMissing.instance;
     }
@@ -198,16 +193,4 @@ public abstract class ConstantNode extends RSourceSectionNode implements RSyntax
             }
         }
     }
-
-    public static Integer asIntConstant(RSyntaxNode argument, boolean castFromDouble) {
-        if (argument instanceof ConstantNode) {
-            Object value = ((ConstantNode) argument).getValue();
-            if (value instanceof Integer) {
-                return (int) value;
-            } else if (castFromDouble && value instanceof Double) {
-                return (int) (double) value;
-            }
-        }
-        return null;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
index 4c7357aab540ba5c9244ceeca17857f63ecdb39d..0b43bf6c4e06d0a319e64c173fec67cedcccd335 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
@@ -39,19 +39,7 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RAttributes;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogical;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RString;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -139,10 +127,6 @@ final class CachedExtractVectorNode extends CachedVectorNode {
                  */
                 return doEnvironment((REnvironment) castVector, positions);
             case Integer:
-                if (castVector instanceof RFactor) {
-                    vector = ((RFactor) castVector).getVector();
-                    break;
-                }
                 vector = (RAbstractContainer) castVector;
                 break;
             default:
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
index a9a7f880199bbbe254747b6b9c6bf593fefa9cac..cc23fc946510bec07252a4d59f56135d1ab37f1a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
@@ -41,20 +41,7 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-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.RPairList;
-import com.oracle.truffle.r.runtime.data.RScalarVector;
-import com.oracle.truffle.r.runtime.data.RShareable;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -146,11 +133,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
                 value = castType.getEmpty();
             }
         } else {
-            if (!isList() && castValue instanceof RFactor) {
-                value = ((RFactor) castValue).getVector();
-            } else {
-                value = (RTypedValue) castValue;
-            }
+            value = (RTypedValue) castValue;
         }
 
         int appliedValueLength;
@@ -183,9 +166,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
             case Null:
                 vector = castType.getEmpty();
                 break;
-            case Factor:
-                vector = ((RFactor) castVector).getVector();
-                break;
             case PairList:
                 vector = ((RPairList) castVector).toRList();
                 break;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
index b2084a03d51f5ae5f39df81c8f72d0af18a2620f..2622dfc83ebb23d9920416d187264533e4ce06fe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
@@ -142,7 +142,6 @@ abstract class CachedVectorNode extends RBaseNode {
         switch (type) {
             case Null:
             case Language:
-            case Factor:
             case PairList:
             case Environment:
             case Expression:
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java
index 8cbb1b12d1d4d09459843b17e4417dd6f7fb5b36..6579b8088ef89d2b44493e1fc01361734f2dfc2e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java
@@ -30,26 +30,8 @@ import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.REmpty;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RInteger;
-import com.oracle.truffle.r.runtime.data.RLogical;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RString;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
@@ -93,11 +75,6 @@ abstract class PositionCastNode extends Node {
         return position;
     }
 
-    @Specialization
-    protected RAbstractVector doFactor(RFactor position) {
-        return position.getVector();
-    }
-
     @Specialization
     protected RAbstractVector doDouble(double position, @Cached("create()") NACheck check) {
         if (mode.isSubscript()) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccess.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccess.java
index 0656f9ee9564215a5fba681332f49c5e9f2588ad..eff4b0c6e2f5e882b6d44441c7f6c68af3d5017c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccess.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccess.java
@@ -47,6 +47,10 @@ public abstract class AttributeAccess extends RBaseNode {
         this.name = name.intern();
     }
 
+    public static AttributeAccess create(String name) {
+        return AttributeAccessNodeGen.create(name);
+    }
+
     public abstract Object execute(RAttributes attr);
 
     protected boolean nameMatches(RAttributes attr, int index) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticNode.java
index 7f5e12b3398960ed20185c99cf21c10092bb0312..a9c8c2fbf982d538e46ef3c846fd7cf8af5f8528 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticNode.java
@@ -31,7 +31,6 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
-import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.primitive.BinaryMapNode;
 import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
@@ -41,19 +40,19 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmeticFactory;
 import com.oracle.truffle.r.runtime.ops.UnaryArithmeticFactory;
 
-public abstract class BinaryArithmeticNode extends RBuiltinNode {
+/**
+ * Represents a binary or unary operation from the 'arithmetic' subset of Ops R group. The concrete
+ * operation is implemented by factory object given as a constructor parameter, e.g.
+ * {@link com.oracle.truffle.r.runtime.ops.BinaryArithmetic.Add}
+ */
+public abstract class BinaryArithmeticNode extends BinaryNodeBase {
 
     protected static final int CACHE_LIMIT = 5;
 
@@ -74,13 +73,15 @@ public abstract class BinaryArithmeticNode extends RBuiltinNode {
         return BinaryArithmeticNodeGen.create(binary, unary, null);
     }
 
-    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(left, right)"})
+    @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(left, right)",
+                    "!isFactor(left)", "!isFactor(right)"})
     protected Object doNumericVectorCached(Object left, Object right, //
                     @Cached("createFastCached(left, right)") BinaryMapNode cached) {
         return cached.apply(left, right);
     }
 
-    @Specialization(contains = "doNumericVectorCached", guards = {"isNumericVector(left)", "isNumericVector(right)"})
+    @Specialization(contains = "doNumericVectorCached", guards = {"isNumericVector(left)", "isNumericVector(right)",
+                    "!isFactor(left)", "!isFactor(right)"})
     @TruffleBoundary
     protected Object doNumericVectorGeneric(Object left, Object right, //
                     @Cached("binary.create()") BinaryArithmetic arithmetic, //
@@ -117,33 +118,25 @@ public abstract class BinaryArithmeticNode extends RBuiltinNode {
         }
     }
 
-    protected static boolean isFactor(Object value) {
-        return value instanceof RFactor;
-    }
-
     @Specialization(guards = "isFactor(left) || isFactor(right)")
-    protected Object doFactor(VirtualFrame frame, Object left, Object right, @Cached("create()") RLengthNode lengthNode) {
+    protected Object doFactor(VirtualFrame frame, RAbstractIntVector left, RAbstractIntVector right, @Cached("create()") RLengthNode lengthNode) {
         Message warning;
-        if (left instanceof RFactor) {
-            warning = getFactorWarning((RFactor) left);
+        if (isFactor(left)) {
+            warning = getFactorWarning(left);
         } else {
-            warning = getFactorWarning((RFactor) right);
+            warning = getFactorWarning(right);
         }
         RError.warning(this, warning, binary.create().opName());
         return RDataFactory.createNAVector(Math.max(lengthNode.executeInteger(frame, left), lengthNode.executeInteger(frame, right)));
     }
 
-    private static Message getFactorWarning(RFactor factor) {
-        return factor.isOrdered() ? Message.NOT_MEANINGFUL_FOR_ORDERED_FACTORS : Message.NOT_MEANINGFUL_FOR_FACTORS;
-    }
-
     @Specialization
     @SuppressWarnings("unused")
     protected static Object doBothNull(RNull left, RNull right) {
         return RType.Double.getEmpty();
     }
 
-    @Specialization(guards = "isNumericVector(right)")
+    @Specialization(guards = {"isNumericVector(right)", "!isFactor(right)"})
     protected static Object doLeftNull(@SuppressWarnings("unused") RNull left, Object right, //
                     @Cached("createClassProfile()") ValueProfile classProfile) {
         if (((RAbstractVector) classProfile.profile(right)).getRType() == RType.Complex) {
@@ -153,7 +146,7 @@ public abstract class BinaryArithmeticNode extends RBuiltinNode {
         }
     }
 
-    @Specialization(guards = "isNumericVector(left)")
+    @Specialization(guards = {"isNumericVector(left)", "!isFactor(left)"})
     protected static Object doRightNull(Object left, RNull right, //
                     @Cached("createClassProfile()") ValueProfile classProfile) {
         return doLeftNull(right, left, classProfile);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
index e6480ed1fdc8aabe8740b72af06938ea81a2ad99..70123f839c15e0b154621bf3daeafa56d7accca9 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanNode.java
@@ -28,8 +28,8 @@ 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.api.frame.VirtualFrame;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
-import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.primitive.BinaryMapNode;
 import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
@@ -39,16 +39,9 @@ import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RInteger;
 import com.oracle.truffle.r.runtime.data.closures.RClosures;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.BinaryCompare;
 import com.oracle.truffle.r.runtime.ops.BinaryLogic;
 import com.oracle.truffle.r.runtime.ops.BinaryLogic.And;
@@ -56,7 +49,13 @@ import com.oracle.truffle.r.runtime.ops.BinaryLogic.Or;
 import com.oracle.truffle.r.runtime.ops.BooleanOperation;
 import com.oracle.truffle.r.runtime.ops.BooleanOperationFactory;
 
-public abstract class BinaryBooleanNode extends RBuiltinNode {
+/**
+ * Represents a binary or unary operation from the 'logical' subset of Ops R group. The concrete
+ * operation is implemented by factory object given as a constructor parameter, e.g.
+ * {@link com.oracle.truffle.r.runtime.ops.BinaryCompare.Equal} or
+ * {@link com.oracle.truffle.r.runtime.ops.BinaryLogic.And}.
+ */
+public abstract class BinaryBooleanNode extends BinaryNodeBase {
 
     protected static final int CACHE_LIMIT = 5;
 
@@ -123,10 +122,6 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
                         (!isLogicOp(factory) && (value instanceof RAbstractStringVector || value instanceof RAbstractRawVector));
     }
 
-    protected static boolean isFactor(Object value) {
-        return value instanceof RFactor;
-    }
-
     @Specialization(guards = {"isRConnection(left) || isRConnection(right)"})
     protected Object doConnection(VirtualFrame frame, Object left, Object right, //
                     @Cached("createRecursive()") BinaryBooleanNode recursive) {
@@ -146,13 +141,15 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
                     @Cached("createRecursive()") BinaryBooleanNode recursive, //
                     @Cached("create()") RAttributeProfiles attrProfiles) {
         Object recursiveLeft = left;
-        if (recursiveLeft instanceof RFactor) {
-            recursiveLeft = RClosures.createFactorToVector((RFactor) recursiveLeft, false, attrProfiles);
+        if (isFactor(left)) {
+            recursiveLeft = RClosures.createFactorToVector((RAbstractIntVector) left, false, attrProfiles);
         }
+
         Object recursiveRight = right;
-        if (recursiveRight instanceof RFactor) {
-            recursiveRight = RClosures.createFactorToVector((RFactor) recursiveRight, false, attrProfiles);
+        if (isFactor(right)) {
+            recursiveRight = RClosures.createFactorToVector((RAbstractIntVector) right, false, attrProfiles);
         }
+
         return recursive.execute(frame, recursiveLeft, recursiveRight);
     }
 
@@ -163,34 +160,32 @@ public abstract class BinaryBooleanNode extends RBuiltinNode {
     @Specialization(guards = {"isFactor(left) || isFactor(right)", "!meaningfulFactorOp(left, right)"})
     protected Object doFactorNotMeaniningful(VirtualFrame frame, Object left, Object right, @Cached("create()") RLengthNode lengthNode) {
         Message warning;
-        if (left instanceof RFactor) {
-            warning = getFactorWarning((RFactor) left);
+        if (isFactor(left)) {
+            warning = getFactorWarning((RAbstractIntVector) left);
         } else {
-            warning = getFactorWarning((RFactor) right);
+            warning = getFactorWarning((RAbstractIntVector) right);
         }
         RError.warning(this, warning, factory.create().opName());
         return RDataFactory.createNAVector(Math.max(lengthNode.executeInteger(frame, left), lengthNode.executeInteger(frame, right)));
     }
 
+    private ConditionProfile meaningfulOpForFactors = ConditionProfile.createBinaryProfile();
+
     protected boolean meaningfulFactorOp(Object left, Object right) {
-        if (factory == BinaryCompare.EQUAL || factory == BinaryCompare.NOT_EQUAL) {
+        if (meaningfulOpForFactors.profile(factory == BinaryCompare.EQUAL || factory == BinaryCompare.NOT_EQUAL)) {
             return true;
-        } else if (left instanceof RFactor) {
-            boolean ordered = ((RFactor) left).isOrdered();
-            if (right instanceof RFactor) {
-                return ordered && ((RFactor) right).isOrdered();
+        } else if (isFactor(left)) {
+            boolean ordered = isOrderedFactor((RAbstractIntVector) left);
+            if (isFactor(right)) {
+                return ordered && isOrderedFactor((RAbstractIntVector) right);
             }
             return ordered;
         } else {
-            assert right instanceof RFactor;
-            return ((RFactor) right).isOrdered();
+            assert isFactor(right) : "meaningfulFactorOp is expected to be invoked with at least one factor.";
+            return isOrderedFactor((RAbstractIntVector) right);
         }
     }
 
-    private static Message getFactorWarning(RFactor factor) {
-        return factor.isOrdered() ? Message.NOT_MEANINGFUL_FOR_ORDERED_FACTORS : Message.NOT_MEANINGFUL_FOR_FACTORS;
-    }
-
     @SuppressWarnings("unused")
     @Specialization(guards = {"isRNullOrEmptyAndNotMissing(left, right)"})
     protected static Object doEmptyOrNull(Object left, Object right) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryNodeBase.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryNodeBase.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e2ff2270da74913382344965eba3973639b1c85
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryNodeBase.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016, 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.binary;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+import com.oracle.truffle.r.nodes.helpers.RFactorNodes;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+
+/**
+ * Provides some shared code for nodes implemented in this package.
+ *
+ * TODO: for the time being, this code is only concerned with R factor class. Code specific to
+ * factors is necessary only because the proper R dispatch of operations implemented in this java
+ * package is not implemented yet. Once this is done, we can remove this base class. It also seems
+ * that {@link BinaryBooleanNode} and {@link BinaryArithmeticNode} are separate classes only to
+ * handle the difference in R specializations that implement them, e.g. Ops.factor handles
+ * 'arithmetic' subset of Ops differently than the 'logical'.
+ */
+abstract class BinaryNodeBase extends RBuiltinNode {
+
+    @Child private InheritsCheckNode factorInheritCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
+    @Child private RFactorNodes.GetOrdered isOrderedFactor = null;
+
+    protected boolean isFactor(Object value) {
+        return factorInheritCheck.execute(value);
+    }
+
+    protected boolean isOrderedFactor(RAbstractIntVector factor) {
+        if (isOrderedFactor == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            isOrderedFactor = insert(new RFactorNodes.GetOrdered());
+        }
+
+        return isOrderedFactor.execute(factor);
+    }
+
+    protected RError.Message getFactorWarning(RAbstractIntVector factor) {
+        return isOrderedFactor(factor) ? RError.Message.NOT_MEANINGFUL_FOR_ORDERED_FACTORS : RError.Message.NOT_MEANINGFUL_FOR_FACTORS;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
index 469e1f11006c8e8e69da4727cc240e876f48f2a7..274546d254248bd72cdbfac3d4569b65c5d4eea5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
@@ -117,9 +117,9 @@ public abstract class RBuiltinNode extends RNode implements VisibilityController
         return Truffle.getRuntime().createCallTarget(root);
     }
 
-    static final RBuiltinNode inline(RBuiltinFactory factory, RNode[] args) {
+    public static final RBuiltinNode inline(RBuiltinDescriptor factory, RNode[] args) {
         // static number of arguments
-        return factory.getConstructor().apply(args);
+        return ((RBuiltinFactory) factory).getConstructor().apply(args);
     }
 
     protected final RBuiltin getRBuiltin() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
index 12f2fcec908deb0fd01b40454efc78dffc0201f2..c502085a2ae82c892dde1e72786bd74617bcac96 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
@@ -31,7 +31,6 @@ import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
-import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public final class RBuiltinRootNode extends RRootNode {
 
@@ -76,10 +75,6 @@ public final class RBuiltinRootNode extends RRootNode {
         return factory.isAlwaysSplit();
     }
 
-    public RBuiltinNode inline(RNode[] args) {
-        return RBuiltinNode.inline(factory, args);
-    }
-
     @Override
     public String getSourceCode() {
         throw RInternalError.shouldNotReachHere();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
index 60c18c59803d2938eb30bfeec9880e070c842264..73dded63d284599fb6fdd1606af7dd5c288d10da 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
@@ -62,9 +62,8 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  * {@link FormalArguments} of a specific function, see
  * {@link #matchArguments(RFunction, UnmatchedArguments, RBaseNode, boolean)} . The other match
  * functions are used for special cases, where builtins make it necessary to re-match parameters,
- * e.g.: {@link #matchArgumentsEvaluated(RFunction, EvaluatedArguments, RBaseNode, boolean)} for
- * 'UseMethod' and {@link #matchArgumentsInlined(RFunction, UnmatchedArguments, RBaseNode)} for
- * builtins which are implemented in Java.
+ * e.g.: {@link #matchArgumentsEvaluated(RFunction, RArgsValuesAndNames, RBaseNode, boolean)} for
+ * 'UseMethod'.
  * </p>
  *
  * <p>
@@ -130,33 +129,13 @@ public class ArgumentMatcher {
      * @return A fresh {@link MatchedArguments} containing the arguments in correct order and
      *         wrapped in {@link PromiseNode}s
      */
-    public static MatchedArguments matchArguments(RFunction function, UnmatchedArguments suppliedArgs, RBaseNode callingNode, boolean noOpt) {
-        RNode[] wrappedArgs = matchNodes(function, suppliedArgs.getArguments(), suppliedArgs.getSignature(), callingNode, suppliedArgs, noOpt);
-        FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
-        return MatchedArguments.create(wrappedArgs, formals.getSignature());
+    public static RNode[] matchArguments(RFunction function, UnmatchedArguments suppliedArgs, RBaseNode callingNode, boolean noOpt) {
+        return matchNodes(function, suppliedArgs.getArguments(), suppliedArgs.getSignature(), callingNode, suppliedArgs, noOpt);
     }
 
-    /**
-     * Match arguments supplied for a specific function call to the formal arguments and wraps them
-     * in special {@link PromiseNode}s. Used for calls to builtins which are built into FastR and
-     * thus are implemented in Java
-     *
-     * @param function The function which is to be called
-     * @param suppliedArgs The arguments supplied to the call
-     * @param callingNode The {@link Node} invoking the match
-     * @return A fresh {@link InlinedArguments} containing the arguments in correct order and
-     *         wrapped in special {@link PromiseNode}s
-     */
-    public static RNode[] matchArgumentsInlined(RFunction function, UnmatchedArguments suppliedArgs, RBaseNode callingNode) {
-        return matchNodes(function, suppliedArgs.getArguments(), suppliedArgs.getSignature(), callingNode, suppliedArgs, false);
-    }
-
-    public static MatchPermutation matchArguments(ArgumentsSignature suppliedSignature, ArgumentsSignature formalSignature, RBaseNode callingNode, boolean forNextMethod, RBuiltinDescriptor builtin) {
+    public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, boolean forNextMethod, RBuiltinDescriptor builtin) {
         CompilerAsserts.neverPartOfCompilation();
-        MatchPermutation match = permuteArguments(suppliedSignature, formalSignature, callingNode, forNextMethod, index -> {
-            throw RInternalError.unimplemented("S3Dispatch should not have arg length mismatch");
-        }, index -> suppliedSignature.getName(index), builtin);
-        return match;
+        return permuteArguments(supplied, formal, callingNode, forNextMethod, index -> false, index -> supplied.getName(index) == null ? "" : supplied.getName(index), builtin);
     }
 
     public static ArgumentsSignature getFunctionSignature(RFunction function) {
@@ -213,10 +192,10 @@ public class ArgumentMatcher {
      * @param callingNode The {@link Node} invoking the match
      * @param forNextMethod matching when evaluating NextMethod
      *
-     * @return A Fresh {@link EvaluatedArguments} containing the arguments rearranged and stuffed
+     * @return A Fresh {@link RArgsValuesAndNames} containing the arguments rearranged and stuffed
      *         with default values (in the form of {@link RPromise}s where needed)
      */
-    public static EvaluatedArguments matchArgumentsEvaluated(RFunction function, EvaluatedArguments evaluatedArgs, RBaseNode callingNode, boolean forNextMethod) {
+    public static RArgsValuesAndNames matchArgumentsEvaluated(RFunction function, RArgsValuesAndNames evaluatedArgs, RBaseNode callingNode, boolean forNextMethod) {
         RRootNode rootNode = (RRootNode) function.getTarget().getRootNode();
         FormalArguments formals = rootNode.getFormalArguments();
         MatchPermutation match = permuteArguments(evaluatedArgs.getSignature(), formals.getSignature(), callingNode, forNextMethod, index -> {
@@ -248,7 +227,7 @@ public class ArgumentMatcher {
                 evaledArgs[formalIndex] = evaluatedArgs.getArgument(suppliedIndex);
             }
         }
-        return new EvaluatedArguments(evaledArgs, formals.getSignature());
+        return new RArgsValuesAndNames(evaledArgs, formals.getSignature());
     }
 
     private static String getErrorForArgument(RNode[] suppliedArgs, ArgumentsSignature suppliedSignature, int index) {
@@ -430,7 +409,7 @@ public class ArgumentMatcher {
         }
     }
 
-    static final class MatchPermutation {
+    public static final class MatchPermutation {
         public static final int UNMATCHED = -1;
         public static final int VARARGS = -2;
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
index 2179fffe501dc67fc0263546c18b0e8731b2834c..a6b47df08fbe1e6680aceccc60eb82e8b5dbc14e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java
@@ -22,10 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.function;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.IdentityHashMap;
-import java.util.List;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -97,33 +95,20 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
      * @param modeChangeForAll
      * @param args {@link #arguments}; new array gets created. Every {@link RNode} (except
      *            <code>null</code>) gets wrapped into a {@link WrapArgumentNode}.
+     * @param varArgsSymbolIndicesArr
      * @return A fresh {@link CallArgumentsNode}
      */
-    public static CallArgumentsNode create(boolean modeChange, boolean modeChangeForAll, RNode[] args, ArgumentsSignature signature) {
+    public static CallArgumentsNode create(boolean modeChange, boolean modeChangeForAll, RNode[] args, ArgumentsSignature signature, int[] varArgsSymbolIndicesArr) {
         // Prepare arguments: wrap in WrapArgumentNode
         RNode[] wrappedArgs = new RNode[args.length];
-        List<Integer> varArgsSymbolIndices = new ArrayList<>();
         for (int i = 0; i < wrappedArgs.length; i++) {
             RNode arg = args[i];
             if (arg == null) {
                 wrappedArgs[i] = null;
             } else {
-                if (arg instanceof ReadVariableNode) {
-                    // Check for presence of "..." in the arguments
-                    ReadVariableNode rvn = (ReadVariableNode) arg;
-                    if (ArgumentsSignature.VARARG_NAME.equals(rvn.getIdentifier())) {
-                        varArgsSymbolIndices.add(i);
-                    }
-                }
                 wrappedArgs[i] = WrapArgumentNode.create(arg, i == 0 || modeChangeForAll ? modeChange : true, i);
             }
         }
-
-        // Setup and return
-        int[] varArgsSymbolIndicesArr = new int[varArgsSymbolIndices.size()];
-        for (int i = 0; i < varArgsSymbolIndicesArr.length; i++) {
-            varArgsSymbolIndicesArr[i] = varArgsSymbolIndices.get(i);
-        }
         return new CallArgumentsNode(wrappedArgs, signature, varArgsSymbolIndicesArr);
     }
 
@@ -140,10 +125,11 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
      * This methods unrolls all "..." in the argument list. The result varies if the number of
      * arguments in the varargs or their names change.
      */
-    public UnrolledVariadicArguments executeFlatten(Frame frame) {
+    public UnmatchedArguments unrollArguments(ArgumentsSignature varArgSignature) {
         CompilerAsserts.neverPartOfCompilation();
+        assert containsVarArgsSymbol() == (varArgSignature != null);
         if (!containsVarArgsSymbol()) {
-            return UnrolledVariadicArguments.create(getArguments(), getSignature(), this);
+            return this;
         } else {
             RNode[] values = new RNode[arguments.length];
             String[] newNames = new String[arguments.length];
@@ -152,19 +138,18 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
             int index = 0;
             for (int i = 0; i < arguments.length; i++) {
                 if (vargsSymbolsIndex < varArgsSymbolIndices.length && varArgsSymbolIndices[vargsSymbolsIndex] == i) {
-                    RArgsValuesAndNames varArgInfo = getVarargsAndNames(frame);
-                    if (varArgInfo.isEmpty()) {
+                    if (varArgSignature.isEmpty()) {
                         // An empty "..." vanishes
                         values = Utils.resizeArray(values, values.length - 1);
                         newNames = Utils.resizeArray(newNames, newNames.length - 1);
                         continue;
                     }
 
-                    values = Utils.resizeArray(values, values.length + varArgInfo.getLength() - 1);
-                    newNames = Utils.resizeArray(newNames, newNames.length + varArgInfo.getLength() - 1);
-                    for (int j = 0; j < varArgInfo.getLength(); j++) {
+                    values = Utils.resizeArray(values, values.length + varArgSignature.getLength() - 1);
+                    newNames = Utils.resizeArray(newNames, newNames.length + varArgSignature.getLength() - 1);
+                    for (int j = 0; j < varArgSignature.getLength(); j++) {
                         values[index] = PromiseNode.createVarArg(j);
-                        newNames[index] = varArgInfo.getSignature().getName(j);
+                        newNames[index] = varArgSignature.getName(j);
                         index++;
                     }
                     vargsSymbolsIndex++;
@@ -174,18 +159,17 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
                     index++;
                 }
             }
-
             return UnrolledVariadicArguments.create(values, ArgumentsSignature.get(newNames), this);
         }
     }
 
     @ExplodeLoop
-    public RArgsValuesAndNames evaluateFlatten(VirtualFrame frame, RArgsValuesAndNames varArgInfo) {
+    public RArgsValuesAndNames evaluateFlatten(VirtualFrame frame, RArgsValuesAndNames varArgs) {
         int size = arguments.length;
         ArgumentsSignature resultSignature = null;
         String[] names = null;
         if (containsVarArgsSymbol()) {
-            size += (varArgInfo.getLength() - 1) * varArgsSymbolIndices.length;
+            size += (varArgs.getLength() - 1) * varArgsSymbolIndices.length;
             names = new String[size];
         } else {
             resultSignature = signature;
@@ -195,7 +179,7 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
         int index = 0;
         for (int i = 0; i < arguments.length; i++) {
             if (vargsSymbolsIndex < varArgsSymbolIndices.length && varArgsSymbolIndices[vargsSymbolsIndex] == i) {
-                index = flattenVarArgs(frame, varArgInfo, names, values, index);
+                index = flattenVarArgs(frame, varArgs, names, values, index);
                 vargsSymbolsIndex++;
             } else {
                 values[index] = arguments[i] == null ? RMissing.instance : arguments[i].execute(frame);
@@ -212,19 +196,17 @@ public final class CallArgumentsNode extends RBaseNode implements UnmatchedArgum
     }
 
     @ExplodeLoop
-    public Object[] evaluateFlattenObjects(VirtualFrame frame) {
+    public Object[] evaluateFlattenObjects(VirtualFrame frame, RArgsValuesAndNames varArgs) {
         int size = arguments.length;
-        RArgsValuesAndNames varArgInfo = null;
         if (containsVarArgsSymbol()) {
-            varArgInfo = getVarargsAndNames(frame);
-            size += (varArgInfo.getLength() - 1) * varArgsSymbolIndices.length;
+            size += (varArgs.getLength() - 1) * varArgsSymbolIndices.length;
         }
         Object[] values = new Object[size];
         int vargsSymbolsIndex = 0;
         int index = 0;
         for (int i = 0; i < arguments.length; i++) {
             if (vargsSymbolsIndex < varArgsSymbolIndices.length && varArgsSymbolIndices[vargsSymbolsIndex] == i) {
-                index = flattenVarArgsObject(frame, varArgInfo, values, index);
+                index = flattenVarArgsObject(frame, varArgs, values, index);
                 vargsSymbolsIndex++;
             } else {
                 values[index] = arguments[i] == null ? RMissing.instance : arguments[i].execute(frame);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
index f5c513c67f84737f621acd6c4f892a0381f0fa8b..3aba0d373115b90028403ad2c73a024a95ef0698 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
@@ -24,7 +24,6 @@ import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RBuiltinRootNode;
 import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation;
 import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
@@ -200,8 +199,7 @@ public abstract class CallMatcherNode extends RBaseNode {
             this.next = next;
             this.formals = ((RRootNode) cachedFunction.getRootNode()).getFormalArguments();
             if (function.isBuiltin()) {
-                RBuiltinRootNode builtinRoot = RCallNode.findBuiltinRootNode(function.getTarget());
-                this.builtin = builtinRoot.inline(null);
+                this.builtin = RBuiltinNode.inline(function.getRBuiltin(), null);
                 this.builtinArgumentCasts = builtin.getCasts();
             } else {
                 this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
@@ -218,7 +216,8 @@ public abstract class CallMatcherNode extends RBaseNode {
                 Object[] reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(permutation, preparedArguments, formals);
                 evaluatePromises(frame, cachedFunction, reorderedArgs, formals.getSignature().getVarArgIndex());
                 if (call != null) {
-                    RCaller caller = functionName == null ? RCallerHelper.InvalidRepresentation.instance : new RCallerHelper.Representation(functionName, reorderedArgs);
+                    RCaller caller = functionName == null ? RCallerHelper.InvalidRepresentation.instance
+                                    : new RCallerHelper.Representation(functionName, new RArgsValuesAndNames(reorderedArgs, ArgumentsSignature.empty(reorderedArgs.length)));
                     Object[] arguments = prepareArguments(frame, reorderedArgs, formals.getSignature(), cachedFunction, dispatchArgs, caller);
                     return call.call(frame, arguments);
                 } else {
@@ -297,10 +296,11 @@ public abstract class CallMatcherNode extends RBaseNode {
 
         @Override
         public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) {
-            EvaluatedArguments reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature);
+            RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature);
             evaluatePromises(frame, function, reorderedArgs.getArguments(), reorderedArgs.getSignature().getVarArgIndex());
 
-            RCaller caller = functionName == null ? RCallerHelper.InvalidRepresentation.instance : new RCallerHelper.Representation(functionName, reorderedArgs.getArguments());
+            RCaller caller = functionName == null ? RCallerHelper.InvalidRepresentation.instance
+                            : new RCallerHelper.Representation(functionName, new RArgsValuesAndNames(reorderedArgs.getArguments(), ArgumentsSignature.empty(reorderedArgs.getLength())));
             Object[] arguments = prepareArguments(frame, reorderedArgs.getArguments(), reorderedArgs.getSignature(), function, dispatchArgs, caller);
             return call.call(frame, function.getTarget(), arguments);
         }
@@ -317,7 +317,7 @@ public abstract class CallMatcherNode extends RBaseNode {
         }
 
         @TruffleBoundary
-        protected EvaluatedArguments reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature) {
+        protected RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature) {
             assert paramSignature.getLength() == args.length;
 
             int argCount = args.length;
@@ -362,10 +362,10 @@ public abstract class CallMatcherNode extends RBaseNode {
             }
 
             // ...and use them as 'supplied' arguments...
-            EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, signature);
+            RArgsValuesAndNames evaledArgs = new RArgsValuesAndNames(argValues, signature);
 
             // ...to match them against the chosen function's formal arguments
-            EvaluatedArguments evaluated = ArgumentMatcher.matchArgumentsEvaluated(function, evaledArgs, this, forNextMethod);
+            RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated(function, evaledArgs, this, forNextMethod);
             return evaluated;
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java
index ca6b156c7254d882a617aeadf605ffc8392466a7..69037c6399a16f843fe699d9e9199287543b6f78 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java
@@ -74,6 +74,10 @@ public abstract class ClassHierarchyNode extends UnaryNode {
         this.withS4 = withS4;
     }
 
+    public static ClassHierarchyNode create() {
+        return ClassHierarchyNodeGen.create(false, false);
+    }
+
     @Override
     public abstract RStringVector execute(Object arg);
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/EvaluatedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/EvaluatedArguments.java
deleted file mode 100644
index 3d20ec3c550d744ad3cc213f5235c0606d719a6d..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/EvaluatedArguments.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2013, 2016, 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.function;
-
-import com.oracle.truffle.r.runtime.Arguments;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
-
-/**
- * Simple container class for holding arguments which are ready to be pushed into {@link RArguments}
- * (or are taken from there!). 'argument missing' is denoted by <code>null</code>.
- */
-public class EvaluatedArguments extends Arguments<Object> {
-
-    EvaluatedArguments(Object[] evaluatedArgs, ArgumentsSignature signature) {
-        super(evaluatedArgs, signature);
-    }
-
-    public static EvaluatedArguments create(Object[] args, ArgumentsSignature signature) {
-        return new EvaluatedArguments(args, signature);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/GetCallerFrameNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
similarity index 91%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/GetCallerFrameNode.java
rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
index b41b824f8bf90adba7b3dc0e5b6740a4ba59f331..cbab2849930de3a423a0482124bb88ff15dfb08e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/GetCallerFrameNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.function.signature;
+package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.frame.Frame;
@@ -30,7 +30,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.PromiseEvalFrameDebug;
-import com.oracle.truffle.r.nodes.function.RCallNode.CallWithCallerFrame;
+import com.oracle.truffle.r.nodes.function.signature.FrameDepthNode;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
@@ -54,8 +54,8 @@ public final class GetCallerFrameNode extends RBaseNode {
             if (frameDepthNode == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
                 RCaller call = RArguments.getCall(frame);
-                if (call != null && call.getSyntaxNode() instanceof CallWithCallerFrame) {
-                    if (!((CallWithCallerFrame) call.getSyntaxNode()).setNeedsCallerFrame()) {
+                if (call instanceof RCallNode) {
+                    if (!((RCallNode) call).setNeedsCallerFrame()) {
                         reset = true;
                     }
                 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
deleted file mode 100644
index 1b104f9bc09a57223d60f70153be6608c6bc836c..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
+++ /dev/null
@@ -1,240 +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) 2014, Purdue University
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import java.util.Arrays;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.UnexpectedResultException;
-import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
-import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.NoGenericMethodException;
-import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
-import com.oracle.truffle.r.runtime.Arguments;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments.S3Args;
-import com.oracle.truffle.r.runtime.RDispatch;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
-
-public final class GroupDispatchNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall {
-
-    @Child private CallArgumentsNode callArgsNode;
-    @Child private S3FunctionLookupNode functionLookupL;
-    @Child private S3FunctionLookupNode functionLookupR;
-    @Child private ClassHierarchyNode classHierarchyL;
-    @Child private ClassHierarchyNode classHierarchyR;
-    @Child private CallMatcherNode callMatcher = CallMatcherNode.create(false, true);
-    @Child private ReadVariableNode lookupVarArgs;
-
-    private final String fixedGenericName;
-    private final RDispatch fixedDispatch;
-    private final RFunction fixedBuiltinFunction;
-
-    private final ConditionProfile mismatchProfile = ConditionProfile.createBinaryProfile();
-
-    @CompilationFinal private boolean dynamicLookup;
-    private final ConditionProfile exactEqualsProfile = ConditionProfile.createBinaryProfile();
-
-    private GroupDispatchNode(SourceSection sourceSection, String genericName, CallArgumentsNode callArgNode, RFunction builtinFunction) {
-        super(sourceSection);
-        this.fixedGenericName = genericName.intern();
-        this.fixedDispatch = builtinFunction.getRBuiltin().getDispatch();
-        this.callArgsNode = callArgNode;
-        this.fixedBuiltinFunction = builtinFunction;
-    }
-
-    public static GroupDispatchNode create(String genericName, SourceSection sourceSection, ArgumentsSignature signature, RSyntaxNode... arguments) {
-        CallArgumentsNode callArgNode = CallArgumentsNode.create(false, true, Arrays.copyOf(arguments, arguments.length, RNode[].class), signature);
-        GroupDispatchNode gdcn = new GroupDispatchNode(sourceSection, genericName, callArgNode, RContext.lookupBuiltin(genericName));
-        return gdcn;
-    }
-
-    public static GroupDispatchNode create(String genericName, CallArgumentsNode callArgNode, RFunction builtinFunction, SourceSection sourceSection) {
-        GroupDispatchNode gdcn = new GroupDispatchNode(sourceSection, genericName, callArgNode, builtinFunction);
-        return gdcn;
-    }
-
-    public Arguments<RSyntaxNode> getArguments() {
-        return new Arguments<>(callArgsNode.getSyntaxArguments(), callArgsNode.getSignature());
-    }
-
-    public String getGenericName() {
-        return fixedGenericName;
-    }
-
-    public SourceSection getCallSrc() {
-        return getSourceSection();
-    }
-
-    @Override
-    public void serializeImpl(RSerialize.State state) {
-        String name = getGenericName();
-        state.setAsBuiltin(name);
-        RCallNode.serializeArguments(state, callArgsNode.getSyntaxArguments(), callArgsNode.signature, false);
-    }
-
-    @Override
-    public RSyntaxNode substituteImpl(REnvironment env) {
-        // TODO substitute aDispatchNode
-        Arguments<RSyntaxNode> substituteArguments = RCallNode.substituteArguments(env, callArgsNode.getSyntaxArguments(), callArgsNode.signature);
-        return RASTUtils.createCall(this, false, substituteArguments.getSignature(), substituteArguments.getArguments());
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        RArgsValuesAndNames varArgs = null;
-        if (callArgsNode.containsVarArgsSymbol()) {
-            if (lookupVarArgs == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                lookupVarArgs = insert(ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any));
-            }
-            try {
-                varArgs = lookupVarArgs.executeRArgsValuesAndNames(frame);
-            } catch (UnexpectedResultException e) {
-                throw RInternalError.shouldNotReachHere(e, "'...' should always be represented by RArgsValuesAndNames");
-            }
-        }
-        RArgsValuesAndNames argAndNames = callArgsNode.evaluateFlatten(frame, varArgs);
-        return executeInternal(frame, argAndNames, fixedGenericName, fixedDispatch, fixedBuiltinFunction);
-    }
-
-    public Object executeDynamic(VirtualFrame frame, RArgsValuesAndNames argAndNames, String genericName, RDispatch dispatch, RFunction builtinFunction) {
-        if (!dynamicLookup) {
-            if (builtinFunction == fixedBuiltinFunction && (exactEqualsProfile.profile(fixedGenericName == genericName) || fixedGenericName.equals(genericName))) {
-                return executeInternal(frame, argAndNames, fixedGenericName, fixedDispatch, fixedBuiltinFunction);
-            }
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            dynamicLookup = true;
-        }
-        return executeInternal(frame, argAndNames, genericName, dispatch, builtinFunction);
-    }
-
-    private Object executeInternal(VirtualFrame frame, RArgsValuesAndNames argAndNames, String genericName, RDispatch dispatch, RFunction builtinFunction) {
-        assert dispatch.isGroupGeneric();
-        Object[] evaluatedArgs = argAndNames.getArguments();
-
-        if (classHierarchyL == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            classHierarchyL = insert(ClassHierarchyNodeGen.create(false, true));
-        }
-        RStringVector typeL = evaluatedArgs.length == 0 ? null : classHierarchyL.execute(evaluatedArgs[0]);
-
-        Result resultL = null;
-        if (typeL != null) {
-            try {
-                if (functionLookupL == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    functionLookupL = insert(S3FunctionLookupNode.create(false, false));
-                }
-                resultL = functionLookupL.execute(frame, genericName, typeL, dispatch.getGroupGenericName(), frame.materialize(), null);
-            } catch (NoGenericMethodException e) {
-                // fall-through
-            }
-        }
-        Result resultR = null;
-        if (dispatch == RDispatch.OPS_GROUP_GENERIC && argAndNames.getSignature().getLength() >= 2) {
-            if (classHierarchyR == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                classHierarchyR = insert(ClassHierarchyNodeGen.create(false, true));
-            }
-            RStringVector typeR = classHierarchyR.execute(evaluatedArgs[1]);
-            if (typeR != null) {
-                try {
-                    if (functionLookupR == null) {
-                        CompilerDirectives.transferToInterpreterAndInvalidate();
-                        functionLookupR = insert(S3FunctionLookupNode.create(false, false));
-                    }
-                    resultR = functionLookupR.execute(frame, genericName, typeR, dispatch.getGroupGenericName(), frame.materialize(), null);
-                } catch (NoGenericMethodException e) {
-                    // fall-through
-                }
-            }
-        }
-
-        Result result;
-        RStringVector dotMethod;
-        if (resultL == null) {
-            if (resultR == null) {
-                result = null;
-                dotMethod = null;
-            } else {
-                result = resultR;
-                dotMethod = RDataFactory.createStringVector(new String[]{"", result.targetFunctionName}, true);
-            }
-        } else {
-            if (resultR == null) {
-                result = resultL;
-                dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, ""}, true);
-            } else {
-                if (mismatchProfile.profile(resultL.function != resultR.function)) {
-                    RError.warning(this, RError.Message.INCOMPATIBLE_METHODS, resultL.targetFunctionName, resultR.targetFunctionName, genericName);
-                    result = null;
-                    dotMethod = null;
-                } else {
-                    result = resultL;
-                    dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, result.targetFunctionName}, true);
-                }
-            }
-        }
-        ArgumentsSignature signature = argAndNames.getSignature();
-        S3Args s3Args;
-        RFunction function;
-        String functionName;
-        if (result == null) {
-            s3Args = null;
-            function = builtinFunction;
-            functionName = function.getName();
-        } else {
-            s3Args = new S3Args(genericName, result.clazz, dotMethod, frame.materialize(), null, result.groupMatch ? dispatch.getGroupGenericName() : null);
-            function = result.function;
-            functionName = result.targetFunctionName;
-        }
-        if (function == null) {
-            CompilerDirectives.transferToInterpreter();
-            throw RError.nyi(this, "missing builtin function '" + genericName + "'");
-        }
-        return callMatcher.execute(frame, signature, evaluatedArgs, function, functionName, s3Args);
-    }
-
-    @Override
-    public RSyntaxElement getSyntaxLHS() {
-        return RSyntaxLookup.createDummyLookup(null, fixedGenericName, true);
-    }
-
-    @Override
-    public RSyntaxElement[] getSyntaxArguments() {
-        return callArgsNode.getSyntaxArguments();
-    }
-
-    @Override
-    public ArgumentsSignature getSyntaxSignature() {
-        return callArgsNode.getSignature();
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/MatchedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/MatchedArguments.java
index 9e248913f67f10c404ff3c20c33f9eae0296d190..fc5b9b5e975fc9b99616cf0475af00f804467b2f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/MatchedArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/MatchedArguments.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
 /**
@@ -43,41 +42,10 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 public final class MatchedArguments extends Arguments<RNode> {
 
-    static final class MatchedArgumentsNode extends RBaseNode {
-        @Children private final RNode[] arguments;
-        private final ArgumentsSignature signature;
-
-        private MatchedArgumentsNode(RNode[] arguments, ArgumentsSignature signature) {
-            this.arguments = arguments;
-            this.signature = signature;
-        }
-
-        @ExplodeLoop
-        Object[] executeArray(VirtualFrame frame) {
-            Object[] result = new Object[arguments.length];
-            for (int i = 0; i < arguments.length; i++) {
-                result[i] = arguments[i].execute(frame);
-            }
-            return result;
-        }
-
-        public ArgumentsSignature getSignature() {
-            return signature;
-        }
-
-        public RNode[] getArguments() {
-            return arguments;
-        }
-    }
-
     private MatchedArguments(RNode[] arguments, ArgumentsSignature signature) {
         super(arguments, signature);
     }
 
-    MatchedArgumentsNode createNode() {
-        return new MatchedArgumentsNode(getArguments(), getSignature());
-    }
-
     /**
      * @return A fresh {@link MatchedArguments}; arguments may contain <code>null</code> iff there
      *         is neither a supplied argument nor a default argument
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index d6e92ce33586dd86896418df7f3e82985033c69d..7117b9d93ec754301f8f7e949ca6323419441b4a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
@@ -24,15 +24,18 @@ package com.oracle.truffle.r.nodes.function;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
-import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerAsserts;
 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.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.NodeChild;
+import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
 import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -41,26 +44,33 @@ import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.DirectCallNode;
-import com.oracle.truffle.api.nodes.IndirectCallNode;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.NodeCloneable;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.NodeUtil;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.nodes.UnexpectedResultException;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.RRootNode;
+import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.FrameSlotNode;
+import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinRootNode;
-import com.oracle.truffle.r.nodes.function.MatchedArguments.MatchedArgumentsNode;
+import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
+import com.oracle.truffle.r.nodes.function.RCallNodeGen.FunctionDispatchNodeGen;
+import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.NoGenericMethodException;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
+import com.oracle.truffle.r.nodes.function.call.PrepareArguments;
 import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
+import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
+import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
@@ -76,9 +86,13 @@ import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
+import com.oracle.truffle.r.runtime.data.RPromise.OptType;
+import com.oracle.truffle.r.runtime.data.RPromise.PromiseType;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -86,182 +100,108 @@ import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-/**
- * This class denotes a call site to a function,e.g.:
- *
- * <pre>
- * f(a, b, c)
- * </pre>
- * <p>
- * To avoid repeated work on each call, {@link CachedCallNode} class together with
- * {@link UninitializedCallNode} forms the basis for the polymorphic inline cache (PIC) used to
- * cache argument nodes on the call site of a function call.
- * </p>
- * There are two problems we're facing which are the reason for the caching:
- * <ol>
- * <li>on some call sites the function may change between executions</li>
- * <li>for call sites taking "..." as an argument the actual argument list passed into the function
- * may change each call (note that this is totally independent of the problem which function is to
- * be called)</li>
- * </ol>
- * <p>
- * Problem 1 can be tackled by a the use of an {@link IndirectCallNode} instead of a
- * {@link DirectCallNode} which is not as performant as the latter but has no further disadvantages.
- * But as the function changed its formal parameters changed, too, so a re-match has to be done as
- * well, which involves the creation of nodes and thus must happen on the {@link TruffleBoundary}.
- * <br/>
- * Problem 2 is not that easy, too: It is solved by reading the values associated with "..." (which
- * are Promises) and wrapping them in newly created {@link RNode}s. These nodes get inserted into
- * the arguments list ({@link CallArgumentsNode#executeFlatten(Frame)}) - which needs to be be
- * matched against the formal parameters again, as theses arguments may carry names as well which
- * may have an impact on argument order. As matching involves node creation, it has to happen on the
- * {@link TruffleBoundary}.
- * </p>
- * To avoid repeated node creations as much as possible by caching two interwoven PICs are
- * implemented. The caches are constructed using the following classes:
- *
- * <pre>
- *  U = {@link UninitializedCallNode}: Forms the uninitialized end of the function PIC
- *  D = {@link DispatchedCallNode}: Function fixed, no varargs
- *  G = {@link GenericCallNode}: Function arbitrary
- *
- *  UV = {@link UninitializedCallNode} with varargs,
- *  UVC = {@link UninitializedVarArgsCacheCallNode} with varargs, for varargs cache
- *  DV = {@link DispatchedVarArgsCallNode}: Function fixed, with cached varargs
- *  DGV = {@link DispatchedGenericVarArgsCallNode}: Function fixed, with arbitrary varargs (generic case)
- *
- * (RB = {@link RBuiltinNode}: individual functions that are builtins are represented by this node
- * which is not aware of caching). Due to {@link CachedCallNode} (see below) this is transparent to
- * the cache and just behaves like a D/DGV)
- * </pre>
- *
- * As the property "takes varargs as argument" is static for each call site, we effectively end up
- * with two separate cache structures. Some examples of each are depicted below:
- *
- * <pre>
- * non varargs, max depth:
- * |
- * D-D-D-U
- *
- * no varargs, generic (if max depth is exceeded):
- * |
- * D-D-D-D-G
- *
- * varargs:
- * |
- * DV-DV-UV         <- function call target identity level cache
- *    |
- *    DV
- *    |
- *    UVC           <- varargs signature level cache
- *
- * varargs, max varargs depth exceeded:
- * |
- * DV-DV-UV
- *    |
- *    DV
- *    |
- *    DV
- *    |
- *    DV
- *    |
- *    DGV
- *
- * varargs, max function depth exceeded:
- * |
- * DV-DV-DV-DV-GV
- * </pre>
- * <p>
- * In the diagrams above every horizontal connection "-" is in fact established by a separate node:
- * {@link CachedCallNode}, which encapsulates the check for function call target identity. So the 1.
- * example in fact looks like this:
- *
- * <pre>
- * |
- * C-C-C-U
- * | | |
- * D D D
- * </pre>
- *
- * This is useful as it avoids repetition and allows child nodes to be more concise. It is also
- * needed as there are {@link RCallNode} implementations that are not aware of caching, e.g.
- * {@link RBuiltinNode}, which may be part of the cache. Note that varargs cache nodes (
- * {@link DispatchedVarArgsCallNode}, the nodes connected by "|") handle the check for varargs value
- * identity by itself.
- * </p>
- *
- * As the two problems of changing arguments and changing functions are totally independent as
- * described above, it would be possible to use two totally separate caching infrastructures. The
- * reason for the current choice is the expectation that R users will try to call same functions
- * with the same arguments, and there will very rarely be the case that the same arguments get
- * passed via "..." to the same functions.
- *
- */
-@NodeInfo(cost = NodeCost.NONE)
-public final class RCallNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall, RCaller {
-
-    private static final int FUNCTION_INLINE_CACHE_SIZE = 4;
-    private static final int VARARGS_INLINE_CACHE_SIZE = 4;
+class ForcePromiseNode extends RNode {
 
-    @Child private RNode functionNode;
+    @Child private RNode valueNode;
     @Child private PromiseHelperNode promiseHelper;
-    @Child private RootCallNode call;
-    @Child private RootCallNode internalDispatchCall;
-    @Child private RNode dispatchArgument;
-    @Child private TemporarySlotNode dispatchTempSlot;
-    @Child private S3FunctionLookupNode dispatchLookup;
-    @Child private ClassHierarchyNode classHierarchyNode;
-    @Child private GroupDispatchNode groupDispatchNode;
+    private final BranchProfile nonPromiseProfile = BranchProfile.create();
 
-    /**
-     * The {@link #arguments} field must be cloned by {@link NodeUtil#cloneNode(Node)}.
-     */
-    private static class SyntaxArguments extends NodeCloneable {
-        private final RSyntaxNode[] v;
+    ForcePromiseNode(RNode valueNode) {
+        this.valueNode = valueNode;
+    }
 
-        SyntaxArguments(RSyntaxNode[] arguments) {
-            this.v = arguments;
-        }
+    public RNode getValueNode() {
+        return valueNode;
+    }
 
-        @Override
-        protected Object clone() {
-            SyntaxArguments sa = new SyntaxArguments(new RSyntaxNode[v.length]);
-            for (int i = 0; i < v.length; i++) {
-                if (v[i] != null) {
-                    RSyntaxNode viClone = (RSyntaxNode) RASTUtils.cloneNode(v[i].asRNode());
-                    sa.v[i] = viClone;
-                }
+    @Override
+    public Object execute(VirtualFrame frame) {
+        Object value = valueNode.execute(frame);
+        if (value instanceof RPromise) {
+            if (promiseHelper == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                promiseHelper = insert(new PromiseHelperNode());
             }
-            return sa;
+            return promiseHelper.evaluate(frame, (RPromise) value);
+        } else {
+            nonPromiseProfile.enter();
+            return value;
         }
     }
+}
+
+interface CallWithCallerFrame extends RCaller {
+    boolean setNeedsCallerFrame();
+}
+
+@NodeInfo(cost = NodeCost.NONE)
+@NodeChild(value = "function", type = ForcePromiseNode.class)
+public abstract class RCallNode extends RNode implements RSyntaxNode, RSyntaxCall, RCaller {
+
+    // currently cannot be RSourceSectionNode because of TruffleDSL restrictions
+
+    @CompilationFinal private SourceSection sourceSectionR;
+
+    @Override
+    public final void setSourceSection(SourceSection sourceSection) {
+        assert sourceSection != null;
+        this.sourceSectionR = sourceSection;
+    }
 
-    private final SyntaxArguments arguments;
+    @Override
+    public final SourceSection getSourceSection() {
+        return sourceSectionR;
+    }
+
+    public abstract Object execute(VirtualFrame frame, Object function);
+
+    protected abstract ForcePromiseNode getFunction();
+
+    private final RSyntaxNode[] arguments;
+    private final int[] varArgIndexes;
     private final ArgumentsSignature signature;
+    @Child private ReadVariableNode lookupVarArgs;
+    protected final LocalReadVariableNode explicitArgs;
+
+    // needed for INTERNAL_GENERIC calls:
+    @Child private FunctionDispatch internalDispatchCall;
+
+    @CompilationFinal private boolean needsCallerFrame;
+
+    boolean setNeedsCallerFrame() {
+        try {
+            return needsCallerFrame;
+        } finally {
+            needsCallerFrame = true;
+        }
+    }
 
-    private final ValueProfile builtinProfile = ValueProfile.createIdentityProfile();
-    private final ConditionProfile implicitTypeProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile resultIsBuiltinProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile isPromiseProfile = ConditionProfile.createBinaryProfile();
-    private final BranchProfile normalDispatchProfile = BranchProfile.create();
-    private final BranchProfile errorProfile = BranchProfile.create();
-    private final ConditionProfile isRFunctionProfile = ConditionProfile.createBinaryProfile();
+    @Override
+    public RSyntaxNode getSyntaxNode() {
+        return this;
+    }
 
-    @Child private Node foreignCall;
-    @CompilationFinal private int foreignCallArgCount;
-    @Child private CallArgumentsNode foreignCallArguments;
+    protected RCaller createCaller(VirtualFrame frame, RFunction function) {
+        if (explicitArgs == null) {
+            return this;
+        } else {
+            return new RCallerHelper.Representation(function, (RArgsValuesAndNames) explicitArgs.execute(frame));
+        }
+    }
 
-    private RCallNode(SourceSection sourceSection, RNode function, RSyntaxNode[] arguments, ArgumentsSignature signature) {
-        super(sourceSection);
-        this.functionNode = function;
-        this.arguments = new SyntaxArguments(arguments);
+    protected RCallNode(SourceSection sourceSection, RSyntaxNode[] arguments, ArgumentsSignature signature) {
+        assert sourceSection != null;
+        this.sourceSectionR = sourceSection;
+        this.arguments = arguments;
+        this.explicitArgs = null;
+        this.varArgIndexes = getVarArgIndexes(arguments);
+        this.lookupVarArgs = varArgIndexes.length == 0 ? null : ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any);
 
         for (String name : signature) {
             if (name != null && name.isEmpty()) {
@@ -275,172 +215,357 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
         this.signature = signature;
     }
 
-    @Override
-    public RSyntaxNode getSyntaxNode() {
-        return this;
+    protected RCallNode(SourceSection sourceSection, Object explicitArgsIdentifier) {
+        assert sourceSection != null;
+        this.sourceSectionR = sourceSection;
+        this.arguments = null;
+        this.explicitArgs = LocalReadVariableNode.create(explicitArgsIdentifier, false);
+        this.varArgIndexes = null;
+        this.lookupVarArgs = null;
+        this.signature = null;
+    }
+
+    public static int[] getVarArgIndexes(RSyntaxNode[] arguments) {
+        List<Integer> varArgsSymbolIndices = new ArrayList<>();
+        for (int i = 0; i < arguments.length; i++) {
+            RSyntaxNode arg = arguments[i];
+            if (arg instanceof RSyntaxLookup) {
+                RSyntaxLookup lookup = (RSyntaxLookup) arg;
+                // Check for presence of "..." in the arguments
+                if (ArgumentsSignature.VARARG_NAME.equals(lookup.getIdentifier())) {
+                    varArgsSymbolIndices.add(i);
+                }
+            }
+        }
+        // Setup and return
+        int[] varArgsSymbolIndicesArr = new int[varArgsSymbolIndices.size()];
+        for (int i = 0; i < varArgsSymbolIndicesArr.length; i++) {
+            varArgsSymbolIndicesArr[i] = varArgsSymbolIndices.get(i);
+        }
+        return varArgsSymbolIndicesArr;
     }
 
     public Arguments<RSyntaxNode> getArguments() {
-        return new Arguments<>(arguments.v, signature);
+        return new Arguments<>(arguments, signature);
     }
 
-    public int getArgumentCount() {
-        return arguments.v.length;
+    private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame) {
+        if (explicitArgs != null) {
+            return (RArgsValuesAndNames) explicitArgs.execute(frame);
+        }
+        RArgsValuesAndNames varArgs;
+        if (lookupVarArgs == null) {
+            varArgs = null;
+        } else {
+            try {
+                varArgs = lookupVarArgs.executeRArgsValuesAndNames(frame);
+            } catch (UnexpectedResultException e) {
+                throw RError.error(RError.SHOW_CALLER, RError.Message.NO_DOT_DOT_DOT);
+            }
+        }
+        return varArgs;
     }
 
-    public RSyntaxNode getArgument(int index) {
-        return arguments.v[index];
+    protected FunctionDispatch createUninitializedCall() {
+        return FunctionDispatchNodeGen.create(this, null, explicitArgs == null ? false : true);
     }
 
-    @Override
-    public Object execute(VirtualFrame frame) {
-        return execute(frame, executeFunctionNode(frame));
+    protected FunctionDispatch createUninitializedExplicitCall() {
+        return FunctionDispatchNodeGen.create(this, null, true);
     }
 
-    @TruffleBoundary
-    private static String getMessage(Throwable e) {
-        return e.getMessage() != null ? e.getMessage() : e.toString();
+    /**
+     * If there are no parameters, or the target function does not refer to a builtin, or the
+     * builtin has no special dispatching, then we know that we will just call the function with no
+     * special dispatch logic.
+     */
+    protected boolean isDefaultDispatch(RFunction function) {
+        return (signature != null && signature.isEmpty()) || function.getRBuiltin() == null || function.getRBuiltin().getDispatch() == RDispatch.DEFAULT;
     }
 
-    @Override
-    public Node deepCopy() {
-        RCallNode copy = (RCallNode) super.deepCopy();
-        // execution (frame) specific due to temp identifiers, so reset
-        copy.internalDispatchCall = null;
-        return copy;
+    @Specialization(guards = "isDefaultDispatch(function)")
+    public Object call(VirtualFrame frame, RFunction function, //
+                    @Cached("createUninitializedCall()") FunctionDispatch call) {
+        return call.execute(frame, function, lookupVarArgs(frame), null);
     }
 
-    public Object execute(VirtualFrame frame, Object functionObject) {
-        RFunction function;
-        if (isRFunctionProfile.profile(functionObject instanceof RFunction)) {
-            function = (RFunction) functionObject;
-            // fall through
-        } else if (functionObject instanceof TruffleObject && !(functionObject instanceof RTypedValue)) {
-            if (foreignCallArguments == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                foreignCallArguments = insert(createArguments(null, true, true));
+    protected RNode createDispatchArgument(int index) {
+        return new ForcePromiseNode(NodeUtil.cloneNode(arguments[index].asRNode()));
+    }
+
+    /**
+     * If the target function refers to a builtin that requires internal generic dispatch and there
+     * are actual parameters to dispatch on, then we will do an internal generic dispatch on the
+     * first parameter.
+     */
+    protected boolean isInternalGenericDispatch(RFunction function) {
+        if (signature != null && signature.isEmpty()) {
+            return false;
+        }
+        RBuiltinDescriptor builtin = function.getRBuiltin();
+        return builtin != null && builtin.getDispatch() == RDispatch.INTERNAL_GENERIC;
+    }
+
+    @Specialization(guards = {"explicitArgs == null", "isInternalGenericDispatch(function)"})
+    public Object callInternalGeneric(VirtualFrame frame, RFunction function, //
+                    @Cached("createDispatchArgument(0)") RNode dispatchArgument, //
+                    @Cached("new()") TemporarySlotNode dispatchTempSlot, //
+                    @Cached("create()") ClassHierarchyNode classHierarchyNode, //
+                    @Cached("createWithError()") S3FunctionLookupNode dispatchLookup, //
+                    @Cached("createIdentityProfile()") ValueProfile builtinProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile resultIsBuiltinProfile) {
+        RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
+        Object dispatchObject = dispatchArgument.execute(frame);
+        FrameSlot slot = dispatchTempSlot.initialize(frame, dispatchObject, () -> internalDispatchCall = null);
+        try {
+            RStringVector type = classHierarchyNode.execute(dispatchObject);
+            S3Args s3Args;
+            RFunction resultFunction;
+            if (implicitTypeProfile.profile(type != null)) {
+                Result result = dispatchLookup.execute(frame, builtin.getName(), type, null, frame.materialize(), null);
+                if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
+                    s3Args = null;
+                } else {
+                    s3Args = new S3Args(result.generic, result.clazz, result.targetFunctionName, frame.materialize(), null, null);
+                }
+                resultFunction = result.function;
+            } else {
+                s3Args = null;
+                resultFunction = function;
             }
-            Object[] argumentsArray = foreignCallArguments.evaluateFlattenObjects(frame);
-            if (foreignCall == null || foreignCallArgCount != argumentsArray.length) {
+            if (internalDispatchCall == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                foreignCall = insert(Message.createExecute(argumentsArray.length).createNode());
-                foreignCallArgCount = argumentsArray.length;
+                internalDispatchCall = insert(FunctionDispatchNodeGen.create(this, new Object[]{dispatchTempSlot.getIdentifier()}, false));
             }
-            try {
-                return ForeignAccess.send(foreignCall, frame, (TruffleObject) functionObject, argumentsArray);
-            } catch (Throwable e) {
-                errorProfile.enter();
-                throw RError.error(this, RError.Message.GENERIC, "Foreign function failed: " + getMessage(e));
+            return internalDispatchCall.execute(frame, resultFunction, lookupVarArgs(frame), s3Args);
+        } finally {
+            dispatchTempSlot.cleanup(frame, slot);
+        }
+    }
+
+    @Specialization(guards = {"explicitArgs != null", "isInternalGenericDispatch(function)"})
+    public Object callInternalGenericExplicit(VirtualFrame frame, RFunction function, //
+                    @Cached("create()") ClassHierarchyNode classHierarchyNode, //
+                    @Cached("createWithError()") S3FunctionLookupNode dispatchLookup, //
+                    @Cached("createIdentityProfile()") ValueProfile builtinProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile resultIsBuiltinProfile, //
+                    @Cached("createPromiseHelper()") PromiseCheckHelperNode promiseHelperNode, //
+                    @Cached("createUninitializedExplicitCall()") FunctionDispatch call) {
+        RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
+        RArgsValuesAndNames argAndNames = (RArgsValuesAndNames) explicitArgs.execute(frame);
+
+        RStringVector type = argAndNames.isEmpty() ? null : classHierarchyNode.execute(promiseHelperNode.checkEvaluate(frame, argAndNames.getArgument(0)));
+        S3Args s3Args;
+        RFunction resultFunction;
+        if (implicitTypeProfile.profile(type != null)) {
+            Result result = dispatchLookup.execute(frame, builtin.getName(), type, null, frame.materialize(), null);
+            if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
+                s3Args = null;
+            } else {
+                s3Args = new S3Args(result.generic, result.clazz, result.targetFunctionName, frame.materialize(), null, null);
             }
+            resultFunction = result.function;
         } else {
-            errorProfile.enter();
-            throw RError.error(RError.SHOW_CALLER, RError.Message.APPLY_NON_FUNCTION);
+            s3Args = null;
+            resultFunction = function;
         }
+        return call.execute(frame, resultFunction, argAndNames, s3Args);
+    }
 
-        if (!signature.isEmpty() && function.getRBuiltin() != null) {
-            RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
-            RDispatch dispatch = builtin.getDispatch();
-            if (dispatch == RDispatch.DEFAULT) {
-                // fallthrough
-            } else if (dispatch == RDispatch.INTERNAL_GENERIC) {
-                if (internalDispatchCall == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    dispatchTempSlot = insert(new TemporarySlotNode());
-                    dispatchArgument = insert(RASTUtils.cloneNode(arguments.v[0].asRNode()));
-                    dispatchLookup = insert(S3FunctionLookupNode.create(true, false));
-                    classHierarchyNode = insert(ClassHierarchyNodeGen.create(false, true));
-                }
-                Object dispatchObject = dispatchArgument.execute(frame);
-                FrameSlot slot = dispatchTempSlot.initialize(frame, dispatchObject, identifier -> internalDispatchCall = insert(new UninitializedCallNode(this, identifier)));
+    protected CallArgumentsNode createArguments() {
+        return signature == null ? null : createArguments(null, false, false);
+    }
+
+    protected ReadVariableNode createVarArgRead(CallArgumentsNode callArguments) {
+        return callArguments.containsVarArgsSymbol() ? ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any) : null;
+    }
+
+    protected boolean isGroupGenericDispatch(RFunction function) {
+        if (signature != null && signature.isEmpty()) {
+            return false;
+        }
+        RBuiltinDescriptor builtin = function.getRBuiltin();
+        return builtin != null && builtin.getDispatch().isGroupGeneric();
+    }
+
+    protected PromiseCheckHelperNode createPromiseHelper() {
+        return new PromiseCheckHelperNode();
+    }
+
+    @Specialization(guards = "isGroupGenericDispatch(function)")
+    public Object callGroupGeneric(VirtualFrame frame, RFunction function, //
+                    @Cached("createArguments()") CallArgumentsNode callArguments, //
+                    @Cached("create()") ClassHierarchyNode classHierarchyNodeX, //
+                    @Cached("createWithException()") S3FunctionLookupNode dispatchLookupX, //
+                    @Cached("create()") ClassHierarchyNode classHierarchyNodeY, //
+                    @Cached("createWithException()") S3FunctionLookupNode dispatchLookupY, //
+                    @Cached("createIdentityProfile()") ValueProfile builtinProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfileX, //
+                    @Cached("createBinaryProfile()") ConditionProfile implicitTypeProfileY, //
+                    @Cached("createBinaryProfile()") ConditionProfile mismatchProfile, //
+                    @Cached("createBinaryProfile()") ConditionProfile resultIsBuiltinProfile, //
+                    @Cached("createPromiseHelper()") PromiseCheckHelperNode promiseHelperNode, //
+                    @Cached("createUninitializedExplicitCall()") FunctionDispatch call) {
+
+        RArgsValuesAndNames argAndNames = explicitArgs != null ? (RArgsValuesAndNames) explicitArgs.execute(frame) : callArguments.evaluateFlatten(frame, lookupVarArgs(frame));
+
+        RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin());
+        RDispatch dispatch = builtin.getDispatch();
+
+        RStringVector typeX = classHierarchyNodeX.execute(promiseHelperNode.checkEvaluate(frame, argAndNames.getArgument(0)));
+        Result resultX = null;
+        if (implicitTypeProfileX.profile(typeX != null)) {
+            try {
+                resultX = dispatchLookupX.execute(frame, builtin.getName(), typeX, dispatch.getGroupGenericName(), frame.materialize(), null);
+            } catch (NoGenericMethodException e) {
+                // fall-through
+            }
+        }
+        Result resultY = null;
+        if (argAndNames.getLength() > 1 && dispatch == RDispatch.OPS_GROUP_GENERIC) {
+            RStringVector typeY = classHierarchyNodeY.execute(promiseHelperNode.checkEvaluate(frame, argAndNames.getArgument(1)));
+            if (implicitTypeProfileY.profile(typeY != null)) {
                 try {
-                    RStringVector type = classHierarchyNode.execute(dispatchObject);
-                    S3Args s3Args;
-                    RFunction resultFunction;
-                    if (implicitTypeProfile.profile(type != null)) {
-                        Result result = dispatchLookup.execute(frame, builtin.getName(), type, null, frame.materialize(), null);
-                        if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
-                            s3Args = null;
-                        } else {
-                            s3Args = new S3Args(result.generic, result.clazz, result.targetFunctionName, frame.materialize(), null, null);
-                        }
-                        resultFunction = result.function;
-                    } else {
-                        s3Args = null;
-                        resultFunction = function;
-                    }
-                    return internalDispatchCall.execute(frame, resultFunction, s3Args);
-                } finally {
-                    dispatchTempSlot.cleanup(frame, slot);
+                    resultY = dispatchLookupY.execute(frame, builtin.getName(), typeY, dispatch.getGroupGenericName(), frame.materialize(), null);
+                } catch (NoGenericMethodException e) {
+                    // fall-through
                 }
+            }
+        }
+
+        Result result;
+        RStringVector dotMethod;
+        if (resultX == null) {
+            if (resultY == null) {
+                result = null;
+                dotMethod = null;
             } else {
-                assert dispatch.isGroupGeneric();
+                result = resultY;
+                dotMethod = RDataFactory.createStringVector(new String[]{"", result.targetFunctionName}, true);
+            }
+        } else {
+            if (resultY == null) {
+                result = resultX;
+                dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, ""}, true);
+            } else {
+                if (mismatchProfile.profile(resultX.function != resultY.function)) {
+                    RError.warning(this, RError.Message.INCOMPATIBLE_METHODS, resultX.targetFunctionName, resultY.targetFunctionName, dispatch.getGroupGenericName());
+                    result = null;
+                    dotMethod = null;
+                } else {
+                    result = resultX;
+                    dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, result.targetFunctionName}, true);
+                }
             }
         }
-        normalDispatchProfile.enter();
-        if (call == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            call = insert(new UninitializedCallNode(this, null));
+        S3Args s3Args;
+        RFunction resultFunction;
+        if (result == null) {
+            s3Args = null;
+            resultFunction = function;
+        } else {
+            if (resultIsBuiltinProfile.profile(result.function.isBuiltin())) {
+                s3Args = null;
+            } else {
+                s3Args = new S3Args(builtin.getName(), result.clazz, dotMethod, frame.materialize(), null, result.groupMatch ? dispatch.getGroupGenericName() : null);
+            }
+            resultFunction = result.function;
         }
-        return call.execute(frame, function, null);
+        return call.execute(frame, resultFunction, argAndNames, s3Args);
     }
 
-    private Object executeFunctionNode(VirtualFrame frame) {
-        /**
-         * Expressions such as "pkg:::f" can result in (delayedAssign) promises that must be
-         * explicitly resolved.
-         */
-        Object value = functionNode.execute(frame);
-        if (isPromiseProfile.profile(value instanceof RPromise)) {
-            if (promiseHelper == null) {
+    protected class ForeignCall extends Node {
+
+        @Child private CallArgumentsNode arguments;
+        @Child private Node foreignCall;
+        @CompilationFinal private int foreignCallArgCount;
+
+        private final BranchProfile errorProfile = BranchProfile.create();
+
+        public ForeignCall(CallArgumentsNode arguments) {
+            this.arguments = arguments;
+        }
+
+        @SuppressWarnings("deprecation")
+        public Object execute(VirtualFrame frame, TruffleObject function) {
+            Object[] argumentsArray = explicitArgs != null ? ((RArgsValuesAndNames) explicitArgs.execute(frame)).getArguments() : arguments.evaluateFlattenObjects(frame, lookupVarArgs(frame));
+            if (foreignCall == null || foreignCallArgCount != argumentsArray.length) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                promiseHelper = insert(new PromiseHelperNode());
+                foreignCall = insert(Message.createExecute(argumentsArray.length).createNode());
+                foreignCallArgCount = argumentsArray.length;
+            }
+            try {
+                return ForeignAccess.execute(foreignCall, frame, function, argumentsArray);
+            } catch (Throwable e) {
+                errorProfile.enter();
+                throw RError.error(this, RError.Message.GENERIC, "Foreign function failed: " + e.getMessage() != null ? e.getMessage() : e.toString());
             }
-            value = promiseHelper.evaluate(frame, (RPromise) value);
         }
-        return value;
+    }
+
+    protected ForeignCall createForeignCall() {
+        return new ForeignCall(createArguments(null, true, true));
+    }
+
+    protected static boolean isRTypedValue(Object value) {
+        return value instanceof RTypedValue;
+    }
+
+    @Specialization(guards = "!isRTypedValue(function)")
+    public Object call(VirtualFrame frame, TruffleObject function, //
+                    @Cached("createForeignCall()") ForeignCall foreignCall) {
+        return foreignCall.execute(frame, function);
+    }
+
+    @TruffleBoundary
+    @Fallback
+    public Object call(@SuppressWarnings("unused") Object function) {
+        throw RError.error(RError.SHOW_CALLER, RError.Message.APPLY_NON_FUNCTION);
     }
 
     public RNode getFunctionNode() {
-        if (functionNode != null) {
+        if (getFunction() != null) {
             // Only the very 1. RootCallNode on the top level contains the one-and-only function
             // node
-            return functionNode;
+            return getFunction().getValueNode();
         }
         return getParentCallNode().getFunctionNode();
     }
 
-    public CallArgumentsNode createArguments(Object dispatchTempIdentifier, boolean modeChange, boolean modeChangeAppliesToAll) {
-        RNode[] args = new RNode[arguments.v.length];
-        for (int i = 0; i < arguments.v.length; i++) {
-            if (i == 0 && dispatchTempIdentifier != null) {
-                args[0] = new GetTempNode(dispatchTempIdentifier, arguments.v[0]);
+    public CallArgumentsNode createArguments(Object[] dispatchTempIdentifiers, boolean modeChange, boolean modeChangeAppliesToAll) {
+        RNode[] args = new RNode[arguments.length];
+        for (int i = 0; i < arguments.length; i++) {
+            if (dispatchTempIdentifiers != null && i < dispatchTempIdentifiers.length) {
+                args[i] = new GetTempNode(dispatchTempIdentifiers[i], arguments[i]);
             } else {
-                args[i] = arguments.v[i] == null ? null : RASTUtils.cloneNode(arguments.v[i].asRNode());
+                args[i] = arguments[i] == null ? null : RASTUtils.cloneNode(arguments[i].asRNode());
             }
         }
-        return CallArgumentsNode.create(modeChange, modeChangeAppliesToAll, args, signature);
+        return CallArgumentsNode.create(modeChange, modeChangeAppliesToAll, args, signature, varArgIndexes);
     }
 
     @Override
     public void serializeImpl(RSerialize.State state) {
         state.setAsLangType();
-        state.serializeNodeSetCar(functionNode);
-        if (isColon(functionNode)) {
+        state.serializeNodeSetCar(getFunctionNode());
+        if (isColon(getFunctionNode())) {
             // special case, have to translate Strings to Symbols
             state.openPairList();
-            state.setCarAsSymbol(((ReadVariableNode) arguments.v[0]).getIdentifier());
+            state.setCarAsSymbol(((ReadVariableNode) arguments[0]).getIdentifier());
             state.openPairList();
-            state.setCarAsSymbol(((ReadVariableNode) arguments.v[1]).getIdentifier());
+            state.setCarAsSymbol(((ReadVariableNode) arguments[1]).getIdentifier());
             state.linkPairList(2);
             state.setCdr(state.closePairList());
         } else {
-            RSyntaxNode f = functionNode.asRSyntaxNode();
+            RSyntaxNode f = getFunctionNode().asRSyntaxNode();
             boolean infixFieldAccess = false;
             if (f instanceof RSyntaxLookup) {
                 RSyntaxLookup lookup = (RSyntaxLookup) f;
                 infixFieldAccess = "$".equals(lookup.getIdentifier()) || "@".equals(lookup.getIdentifier());
             }
-            serializeArguments(state, arguments.v, signature, infixFieldAccess);
+            serializeArguments(state, arguments, signature, infixFieldAccess);
         }
     }
 
@@ -490,7 +615,7 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
     public RSyntaxNode substituteImpl(REnvironment env) {
         RNode functionSub = getFunctionNode().substitute(env).asRNode();
 
-        Arguments<RSyntaxNode> argsSub = substituteArguments(env, arguments.v, signature);
+        Arguments<RSyntaxNode> argsSub = substituteArguments(env, arguments, signature);
         return RASTUtils.createCall(functionSub, false, argsSub.getSignature(), argsSub.getArguments());
     }
 
@@ -521,15 +646,45 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
      * Creates a call to a resolved {@link RBuiltinKind#INTERNAL} that will be used to replace the
      * original call.
      *
-     * @param frame The frame to create the inlined builtins in
-     * @param internalCallArg the {@link UninitializedCallNode} corresponding to the argument to the
-     *            {code .Internal}.
+     * @param internalCallArg the {@link RCallNode} corresponding to the argument to the {code
+     *            .Internal}.
      * @param function the resolved {@link RFunction}.
-     * @param name The name of the function
      */
-    public static LeafCallNode createInternalCall(VirtualFrame frame, RCallNode internalCallArg, RFunction function, String name) {
+    public static RNode createInternalCall(RCallNode internalCallArg, RFunction function) {
         CompilerAsserts.neverPartOfCompilation();
-        return UninitializedCallNode.createCacheNode(frame, internalCallArg.createArguments(null, false, true), internalCallArg, function);
+        return new InternalNode(FunctionDispatchNodeGen.create(internalCallArg, null, false), function, internalCallArg.lookupVarArgs != null);
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    private static final class InternalNode extends RNode {
+        @Child private FunctionDispatch rootCallNode;
+        @Child private ReadVariableNode lookupVarArgs;
+        private final RFunction function;
+
+        InternalNode(FunctionDispatch rootCallNode, RFunction function, boolean needsVarArgLookup) {
+            this.rootCallNode = rootCallNode;
+            this.function = function;
+            this.lookupVarArgs = needsVarArgLookup ? ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any) : null;
+        }
+
+        private RArgsValuesAndNames lookupVarArgs(VirtualFrame frame) {
+            RArgsValuesAndNames varArgs;
+            if (lookupVarArgs == null) {
+                varArgs = null;
+            } else {
+                try {
+                    varArgs = lookupVarArgs.executeRArgsValuesAndNames(frame);
+                } catch (UnexpectedResultException e) {
+                    throw RError.error(RError.SHOW_CALLER, RError.Message.NO_DOT_DOT_DOT);
+                }
+            }
+            return varArgs;
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return rootCallNode.execute(frame, function, lookupVarArgs(frame), null);
+        }
     }
 
     /**
@@ -539,11 +694,11 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
      */
     @TruffleBoundary
     public static RCallNode createCloneReplacingArgs(RCallNode call, RSyntaxNode... replacementArgs) {
-        RSyntaxNode[] args = new RSyntaxNode[call.arguments.v.length];
+        RSyntaxNode[] args = new RSyntaxNode[call.arguments.length];
         for (int i = 0; i < args.length; i++) {
-            args[i] = i < replacementArgs.length ? replacementArgs[i] : call.arguments.v[i];
+            args[i] = i < replacementArgs.length ? replacementArgs[i] : call.arguments[i];
         }
-        return new RCallNode(call.getSourceSection(), RASTUtils.cloneNode(call.functionNode), args, call.signature);
+        return RCallNodeGen.create(call.getSourceSection(), args, call.signature, new ForcePromiseNode(RASTUtils.cloneNode(call.getFunction())));
     }
 
     /**
@@ -551,13 +706,17 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
      * {@code src == RSyntaxNode.EAGER_DEPARSE} we force a deparse.
      */
     public static RCallNode createCall(SourceSection src, RNode function, ArgumentsSignature signature, RSyntaxNode... arguments) {
-        RCallNode call = new RCallNode(src, function, arguments, signature);
+        RCallNode call = RCallNodeGen.create(src, arguments, signature, new ForcePromiseNode(function));
         if (src == RSyntaxNode.EAGER_DEPARSE) {
             RDeparse.ensureSourceSection(call);
         }
         return call;
     }
 
+    public static RCallNode createExplicitCall(Object explicitArgsIdentifier) {
+        return RCallNodeGen.create(RSyntaxNode.INTERNAL, explicitArgsIdentifier, null);
+    }
+
     static RBuiltinRootNode findBuiltinRootNode(RootCallTarget callTarget) {
         RootNode root = callTarget.getRootNode();
         if (root instanceof RBuiltinRootNode) {
@@ -584,12 +743,6 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
         return false;
     }
 
-    public abstract static class RootCallNode extends Node {
-
-        public abstract Object execute(VirtualFrame frame, RFunction function, S3Args s3Args);
-
-    }
-
     private static final class GetTempNode extends RNode {
 
         @Child private FrameSlotNode slot;
@@ -615,138 +768,83 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
         }
     }
 
-    /**
-     * [U]/[UV] Forms the uninitialized end of the function PIC.
-     *
-     * @see RCallNode
-     */
-    @NodeInfo(cost = NodeCost.UNINITIALIZED)
-    private static final class UninitializedCallNode extends RootCallNode {
+    public abstract static class FunctionDispatch extends Node {
 
-        private int depth;
-        private final RCallNode call;
-        private final Object dispatchTempIdentifier;
+        public abstract Object execute(VirtualFrame frame, RFunction function, Object varArgs, Object s3Args);
 
-        UninitializedCallNode(RCallNode call, Object dispatchTempIdentifier) {
-            this.call = call;
-            this.dispatchTempIdentifier = dispatchTempIdentifier;
-            this.depth = 0;
-        }
+        protected static final int CACHE_SIZE = 4;
 
-        @Override
-        public Object execute(VirtualFrame frame, RFunction function, S3Args s3Args) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            depth++;
-            RootCallNode next = depth < FUNCTION_INLINE_CACHE_SIZE ? this : new GenericCallNode(call.createArguments(dispatchTempIdentifier, true, true), call);
-
-            RBuiltinDescriptor builtin = function.getRBuiltin();
-            boolean modeChange = true;
-            if (builtin != null) {
-                modeChange = false;
-            }
+        private final RCallNode originalCall;
+        private final Object[] dispatchTempIdentifiers;
+        private final boolean explicitArgs;
 
-            LeafCallNode current = createCacheNode(frame, call.createArguments(dispatchTempIdentifier, modeChange, true), call, function);
-            RootCallNode cachedNode = new CachedCallNode(current, next, function);
-            this.replace(cachedNode);
-            return cachedNode.execute(frame, function, s3Args);
+        public FunctionDispatch(RCallNode originalCall, Object[] dispatchTempIdentifiers, boolean explicitArgs) {
+            this.originalCall = originalCall;
+            this.dispatchTempIdentifiers = dispatchTempIdentifiers;
+            this.explicitArgs = explicitArgs;
         }
 
-        private static LeafCallNode createCacheNode(VirtualFrame frame, CallArgumentsNode args, RCallNode creator, RFunction function) {
-            CompilerDirectives.transferToInterpreter();
-
-            LeafCallNode callNode = null;
-            // Check implementation: If written in Java, handle differently!
+        protected LeafCallNode createCacheNode(RFunction function) {
+            CompilerAsserts.neverPartOfCompilation();
+            FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
             if (function.isBuiltin()) {
-                RootCallTarget callTarget = function.getTarget();
-                RBuiltinRootNode root = findBuiltinRootNode(callTarget);
-                assert root != null;
-
-                // We inline the given arguments here, as builtins are executed inside the same
-                // frame as they are called.
-                RNode[] inlinedArgs = ArgumentMatcher.matchArgumentsInlined(function, args, creator);
-                callNode = new BuiltinCallNode(root.inline(inlinedArgs), creator);
+                return new BuiltinCallNode(RBuiltinNode.inline(function.getRBuiltin(), null), function.getRBuiltin(), formals, originalCall);
             } else {
-                // Now we need to distinguish: Do supplied arguments vary between calls?
-                if (args.containsVarArgsSymbol()) {
-                    VarArgsCacheCallNode nextNode = new UninitializedVarArgsCacheCallNode(args, creator);
-                    ArgumentsSignature varArgsSignature = CallArgumentsNode.getVarargsAndNames(frame).getSignature();
-                    callNode = DispatchedVarArgsCallNode.create(frame, args, nextNode, creator, function, varArgsSignature);
-                } else {
-                    MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(function, args, creator, false);
-                    callNode = new DispatchedCallNode(function, matchedArgs, creator);
-                }
+                return new DispatchedCallNode(function, formals.getSignature(), originalCall);
             }
-
-            return callNode;
         }
-    }
-
-    /**
-     * [C] Extracts the check for function call target identity away from the individual cache
-     * nodes.
-     *
-     * @see RCallNode
-     */
-    private static final class CachedCallNode extends RootCallNode {
-
-        @Child private RootCallNode nextNode;
-        @Child private LeafCallNode currentNode;
-
-        private final CallTarget cachedCallTarget;
 
-        CachedCallNode(LeafCallNode current, RootCallNode next, RFunction cachedFunction) {
-            this.currentNode = current;
-            this.nextNode = next;
-            this.cachedCallTarget = cachedFunction.getTarget();
+        protected PrepareArguments createArguments(RFunction function) {
+            if (explicitArgs) {
+                return PrepareArguments.createExplicit(function);
+            } else {
+                CallArgumentsNode args = originalCall.createArguments(dispatchTempIdentifiers, !function.isBuiltin(), true);
+                return PrepareArguments.create(function, args);
+            }
         }
 
-        @Override
-        public Object execute(VirtualFrame frame, RFunction f, S3Args s3Args) {
-            if (cachedCallTarget == f.getTarget()) {
-                return currentNode.execute(frame, f, s3Args);
-            }
-            return nextNode.execute(frame, f, s3Args);
+        @Specialization(limit = "CACHE_SIZE", guards = "function == cachedFunction")
+        protected Object dispatch(VirtualFrame frame, @SuppressWarnings("unused") RFunction function, Object varArgs, Object s3Args, //
+                        @Cached("function") RFunction cachedFunction, //
+                        @Cached("createCacheNode(cachedFunction)") LeafCallNode leafCall, //
+                        @Cached("createArguments(cachedFunction)") PrepareArguments prepareArguments) {
+            Object[] orderedArguments = prepareArguments.execute(frame, (RArgsValuesAndNames) varArgs, originalCall);
+            return leafCall.execute(frame, cachedFunction, orderedArguments, (S3Args) s3Args);
         }
-    }
 
-    /**
-     * [GV] {@link RCallNode} in case there is no fixed {@link RFunction} AND varargs...
-     *
-     * @see RCallNode
-     */
-    private static final class GenericCallNode extends RootCallNode {
+        /*
+         * Use a TruffleBoundaryNode to be able to switch child nodes without invalidating the whole
+         * method.
+         */
+        protected final class GenericCall extends TruffleBoundaryNode {
 
-        @Child private IndirectCallNode indirectCall = Truffle.getRuntime().createIndirectCallNode();
-        @Child private CallArgumentsNode arguments;
+            private RFunction cachedFunction;
+            @Child private LeafCallNode leafCall;
+            @Child private PrepareArguments prepareArguments;
 
-        private final RCallNode call;
+            @TruffleBoundary
+            public Object execute(MaterializedFrame materializedFrame, RFunction function, Object varArgs, Object s3Args) {
+                if (cachedFunction != function) {
+                    leafCall = insert(createCacheNode(function));
+                    prepareArguments = insert(createArguments(function));
+                }
+                VirtualFrame frame = SubstituteVirtualFrame.create(materializedFrame);
+                Object[] orderedArguments = prepareArguments.execute(frame, (RArgsValuesAndNames) varArgs, originalCall);
+                return leafCall.execute(frame, cachedFunction, orderedArguments, (S3Args) s3Args);
+            }
+        }
 
-        GenericCallNode(CallArgumentsNode arguments, RCallNode call) {
-            // here, it's safe to reuse the arguments - it's the final use
-            this.arguments = arguments;
-            this.call = call;
+        protected GenericCall createGenericCall() {
+            return new GenericCall();
         }
 
-        @Override
-        public Object execute(VirtualFrame frame, RFunction currentFunction, S3Args s3Args) {
-            CompilerDirectives.transferToInterpreter();
-            // Function and arguments may change every call: Flatt'n'Match on SlowPath! :-/
-            UnrolledVariadicArguments argsValuesAndNames = arguments.executeFlatten(frame);
-            MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(currentFunction, argsValuesAndNames, RError.ROOTNODE, true);
-
-            Object[] argsObject = RArguments.create(currentFunction, call, null, RArguments.getDepth(frame) + 1, RArguments.getPromiseFrame(frame), matchedArgs.doExecuteArray(frame),
-                            matchedArgs.getSignature(), s3Args);
-            return indirectCall.call(frame, currentFunction.getTarget(), argsObject);
+        @Specialization
+        protected Object dispatchFallback(VirtualFrame frame, RFunction function, Object varArgs, Object s3Args, //
+                        @Cached("createGenericCall()") GenericCall generic) {
+            return generic.execute(frame.materialize(), function, varArgs, s3Args);
         }
     }
 
-    /**
-     * This is the counterpart of {@link RCallNode}: While that is the base class for all top-level
-     * nodes (C, G, U and GV), this is the base class for all nodes not on the top level of the PIC
-     * (and thus don't need all information, but can rely on their parents to have them).
-     *
-     * @see RCallNode
-     */
     public abstract static class LeafCallNode extends RBaseNode {
         /**
          * The original {@link RSyntaxNode} this derives from.
@@ -762,297 +860,162 @@ public final class RCallNode extends RSourceSectionNode implements RSyntaxNode,
             return originalCall;
         }
 
-        public abstract Object execute(VirtualFrame frame, RFunction function, S3Args s3Args);
+        public abstract Object execute(VirtualFrame frame, RFunction function, Object[] orderedArguments, S3Args s3Args);
     }
 
     @NodeInfo(cost = NodeCost.NONE)
     private static final class BuiltinCallNode extends LeafCallNode {
 
         @Child private RBuiltinNode builtin;
+        @Child private PromiseCheckHelperNode promiseHelper;
+        @Children private final CastNode[] casts;
 
-        BuiltinCallNode(RBuiltinNode builtin, RCallNode originalCall) {
-            super(originalCall);
-            this.builtin = builtin;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RFunction currentFunction, S3Args s3Args) {
-            return builtin.execute(frame);
-        }
-    }
-
-    public interface CallWithCallerFrame {
-        boolean setNeedsCallerFrame();
-    }
-
-    /**
-     * [D] A {@link RCallNode} for calls to fixed {@link RFunction}s with fixed arguments (no
-     * varargs).
-     *
-     * @see RCallNode
-     */
-    private static final class DispatchedCallNode extends LeafCallNode implements CallWithCallerFrame {
-
-        @Child private DirectCallNode call;
-        @Child private MatchedArgumentsNode matchedArgs;
-        @Child private RArgumentsNode argsNode = RArgumentsNode.create();
-        @Child private RFastPathNode fastPath;
-
-        @CompilationFinal private boolean needsCallerFrame;
-        private final RootCallTarget callTarget;
-        @CompilationFinal private boolean needsSplitting;
+        private final BranchProfile emptyProfile = BranchProfile.create();
+        private final BranchProfile varArgsProfile = BranchProfile.create();
+        private final ConditionProfile wrapProfile = ConditionProfile.createBinaryProfile();
+        private final FormalArguments formals;
+        private final RBuiltinDescriptor builtinDescriptor;
 
-        DispatchedCallNode(RFunction function, MatchedArguments matchedArgs, RCallNode originalCall) {
+        BuiltinCallNode(RBuiltinNode builtin, RBuiltinDescriptor builtinDescriptor, FormalArguments formalArguments, RCallNode originalCall) {
             super(originalCall);
-            this.matchedArgs = matchedArgs.createNode();
-            this.needsCallerFrame = function.containsDispatch();
-            this.needsSplitting = needsSplitting(function);
-            this.callTarget = function.getTarget();
+            this.builtin = builtin;
+            this.builtinDescriptor = builtinDescriptor;
+            this.casts = builtin.getCasts();
+            this.formals = formalArguments;
+        }
+
+        @ExplodeLoop
+        public Object[] castArguments(VirtualFrame frame, Object[] args) {
+            int argCount = formals.getLength();
+            Object[] result = new Object[argCount];
+            for (int i = 0; i < argCount; i++) {
+                Object arg = args[i];
+                if (arg == REmpty.instance) {
+                    emptyProfile.enter();
+                    arg = formals.getInternalDefaultArgumentAt(i);
+                }
+                if (arg instanceof RArgsValuesAndNames) {
+                    varArgsProfile.enter();
+                    RArgsValuesAndNames varArgs = (RArgsValuesAndNames) arg;
+                    if (builtinDescriptor.evaluatesArg(i)) {
+                        forcePromises(frame, varArgs);
+                    } else {
+                        wrapPromises(varArgs);
+                    }
+                } else {
+                    if (builtinDescriptor.evaluatesArg(i)) {
+                        if (arg instanceof RPromise) {
+                            if (promiseHelper == null) {
+                                CompilerDirectives.transferToInterpreterAndInvalidate();
+                                promiseHelper = insert(new PromiseCheckHelperNode());
+                            }
+                            arg = promiseHelper.checkEvaluate(frame, arg);
 
-            this.fastPath = function.getFastPath() == null ? null : function.getFastPath().create();
-            if (fastPath == null) {
-                this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
+                        }
+                        if (i < casts.length && casts[i] != null) {
+                            assert builtinDescriptor.evaluatesArg(i);
+                            arg = casts[i].execute(arg);
+                        }
+                    } else {
+                        assert casts.length <= i || casts[i] == null : "no casts allowed on non-evaluated arguments";
+                        if (wrapProfile.profile(!(arg instanceof RPromise || arg instanceof RMissing))) {
+                            arg = createPromise(arg);
+                        }
+                    }
+                }
+                result[i] = arg;
             }
+            return result;
         }
 
-        @Override
-        public Object execute(VirtualFrame frame, RFunction currentFunction, S3Args s3Args) {
-            if (fastPath != null) {
-                Object[] argValues = matchedArgs.executeArray(frame);
-                Object result = fastPath.execute(frame, argValues);
-                if (result != null) {
-                    return result;
+        private void forcePromises(VirtualFrame frame, RArgsValuesAndNames varArgs) {
+            Object[] array = varArgs.getArguments();
+            for (int i = 0; i < array.length; i++) {
+                if (promiseHelper == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    promiseHelper = insert(new PromiseCheckHelperNode());
                 }
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                fastPath = null;
-                call = insert(Truffle.getRuntime().createDirectCallNode(callTarget));
-            }
-
-            if (needsSplitting) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                needsSplitting = false;
-                call.cloneCallTarget();
+                array[i] = promiseHelper.checkEvaluate(frame, array[i]);
             }
-            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
-
-            Object[] argsObject = argsNode.execute(currentFunction, originalCall, callerFrame, RArguments.getDepth(frame) + 1, RArguments.getPromiseFrame(frame),
-                            matchedArgs.executeArray(frame),
-                            matchedArgs.getSignature(), s3Args);
-            return call.call(frame, argsObject);
         }
 
-        @Override
-        public boolean setNeedsCallerFrame() {
-            try {
-                return needsCallerFrame;
-            } finally {
-                needsCallerFrame = true;
+        @TruffleBoundary
+        private static void wrapPromises(RArgsValuesAndNames varArgs) {
+            Object[] array = varArgs.getArguments();
+            for (int i = 0; i < array.length; i++) {
+                Object arg = array[i];
+                if (!(arg instanceof RPromise || arg instanceof RMissing)) {
+                    array[i] = createPromise(arg);
+                }
             }
         }
-    }
-
-    /*
-     * Varargs cache classes follow
-     */
-
-    /**
-     * Base class for the varargs cache [UVC] and [DV].
-     *
-     * @see RCallNode
-     */
-    private abstract static class VarArgsCacheCallNode extends LeafCallNode {
-
-        @Child private ReadVariableNode lookupVarArgs = ReadVariableNode.createSilent(ArgumentsSignature.VARARG_NAME, RType.Any);
 
-        private VarArgsCacheCallNode(RCallNode originalCallNode) {
-            super(originalCallNode);
+        @TruffleBoundary
+        private static Object createPromise(Object arg) {
+            return RDataFactory.createPromise(PromiseType.NO_ARG, OptType.PROMISED, ConstantNode.create(arg), arg);
         }
 
         @Override
-        public final Object execute(VirtualFrame frame, RFunction function, S3Args s3Args) {
-            Object varArgs = lookupVarArgs.execute(frame);
-            if (varArgs == RNull.instance) {
-                varArgs = RArgsValuesAndNames.EMPTY;
-            }
-            if (varArgs == null || !(varArgs instanceof RArgsValuesAndNames)) {
-                CompilerDirectives.transferToInterpreter();
-                RError.error(RError.SHOW_CALLER, RError.Message.NO_DOT_DOT_DOT);
-            }
-            return execute(frame, function, ((RArgsValuesAndNames) varArgs).getSignature(), s3Args);
+        public Object execute(VirtualFrame frame, RFunction currentFunction, Object[] orderedArguments, S3Args s3Args) {
+            return builtin.execute(frame, castArguments(frame, orderedArguments));
         }
-
-        protected abstract Object execute(VirtualFrame frame, RFunction function, ArgumentsSignature varArgsSignature, S3Args s3Args);
     }
 
-    /**
-     * [UVC] The uninitialized end of the varargs cache.
-     *
-     * @see RCallNode
-     */
-    @NodeInfo(cost = NodeCost.UNINITIALIZED)
-    private static final class UninitializedVarArgsCacheCallNode extends VarArgsCacheCallNode {
-        @Child private CallArgumentsNode args;
-        private int depth = 1;  // varargs cached is started with a [DV] DispatchedVarArgsCallNode
-
-        UninitializedVarArgsCacheCallNode(CallArgumentsNode args, RCallNode creator) {
-            super(creator);
-            this.args = args;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RFunction function, ArgumentsSignature varArgsSignature, S3Args s3Args) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-
-            // Extend cache
-            this.depth += 1;
-            CallArgumentsNode clonedArgs = RASTUtils.cloneNode(args);
-            VarArgsCacheCallNode next = createNextNode(function);
-            DispatchedVarArgsCallNode newCallNode = DispatchedVarArgsCallNode.create(frame, clonedArgs, next, this, function, varArgsSignature);
-            return replace(newCallNode).execute(frame, function, varArgsSignature, s3Args);
-        }
-
-        private VarArgsCacheCallNode createNextNode(RFunction function) {
-            if (depth < VARARGS_INLINE_CACHE_SIZE) {
-                return this;
-            } else {
-                CallArgumentsNode clonedArgs = RASTUtils.cloneNode(args);
-                return new DispatchedGenericVarArgsCallNode(function, clonedArgs, originalCall);
-            }
-        }
-    }
+    private static final class DispatchedCallNode extends LeafCallNode {
 
-    /**
-     * [DV] A {@link RCallNode} for calls to cached {@link RFunction}s with cached varargs.
-     *
-     * @see RCallNode
-     */
-    private static final class DispatchedVarArgsCallNode extends VarArgsCacheCallNode implements CallWithCallerFrame {
         @Child private DirectCallNode call;
-        @Child private CallArgumentsNode args;
-        @Child private VarArgsCacheCallNode next;
-        @Child private MatchedArgumentsNode matchedArgs;
-        @Child private RArgumentsNode argsNode = RArgumentsNode.create();
+        @Child private RArgumentsNode argsNode;
+        @Child private RFastPathNode fastPath;
 
-        private final ArgumentsSignature cachedSignature;
-        @CompilationFinal private boolean needsCallerFrame;
-        @CompilationFinal private boolean needsSplitting;
+        private final ArgumentsSignature signature;
+        private final RFunction cachedFunction;
 
-        protected DispatchedVarArgsCallNode(CallArgumentsNode args, VarArgsCacheCallNode next, RFunction function, ArgumentsSignature varArgsSignature, MatchedArguments matchedArgs,
-                        RCallNode originalCall) {
+        DispatchedCallNode(RFunction function, ArgumentsSignature signature, RCallNode originalCall) {
             super(originalCall);
-            this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
-            this.args = args;
-            this.next = next;
-            this.cachedSignature = varArgsSignature;
-            this.matchedArgs = matchedArgs.createNode();
-            this.needsCallerFrame = function.containsDispatch();
-            /*
-             * this is a simple heuristic - methods that need a caller frame should have call site -
-             * specific versions
-             */
-            this.needsSplitting = needsSplitting(function);
-        }
-
-        protected static DispatchedVarArgsCallNode create(VirtualFrame frame, CallArgumentsNode args, VarArgsCacheCallNode next, RBaseNode creator, RFunction function,
-                        ArgumentsSignature varArgsSignature) {
-            UnrolledVariadicArguments unrolledArguments = args.executeFlatten(frame);
-            MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(function, unrolledArguments, creator, false);
-            RCallNode originalCall;
-            if (creator instanceof LeafCallNode) {
-                originalCall = ((LeafCallNode) creator).originalCall;
-            } else if (creator instanceof RCallNode) {
-                originalCall = (RCallNode) creator;
-            } else {
-                throw RInternalError.shouldNotReachHere();
-            }
-            return new DispatchedVarArgsCallNode(args, next, function, varArgsSignature, matchedArgs, originalCall);
+            this.cachedFunction = function;
+            this.signature = signature;
+            this.fastPath = function.getFastPath() == null ? null : function.getFastPath().create();
+            originalCall.needsCallerFrame |= cachedFunction.containsDispatch();
         }
 
         @Override
-        public Object execute(VirtualFrame frame, RFunction currentFunction, ArgumentsSignature varArgsSignature, S3Args s3Args) {
-            // If this is the root of the varargs sub-cache: The signature needs to be created
-            // once
-
-            // If the signature does not match: delegate to next node!
-            if (cachedSignature != varArgsSignature) {
-                return next.execute(frame, currentFunction, varArgsSignature, s3Args);
-            }
-
-            // Our cached function and matched arguments do match, simply execute!
-
-            if (needsSplitting) {
+        public Object execute(VirtualFrame frame, RFunction currentFunction, Object[] orderedArguments, S3Args s3Args) {
+            if (fastPath != null) {
+                Object result = fastPath.execute(frame, orderedArguments);
+                if (result != null) {
+                    return result;
+                }
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                needsSplitting = false;
-                call.cloneCallTarget();
-            }
-            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
-            Object[] argsObject = argsNode.execute(currentFunction, originalCall, callerFrame, RArguments.getDepth(frame) + 1, RArguments.getPromiseFrame(frame),
-                            matchedArgs.executeArray(frame),
-                            matchedArgs.getSignature(), s3Args);
-            return call.call(frame, argsObject);
-        }
-
-        @Override
-        public boolean setNeedsCallerFrame() {
-            try {
-                return needsCallerFrame;
-            } finally {
-                needsCallerFrame = true;
+                fastPath = null;
             }
-        }
-    }
 
-    /**
-     * [DGV] {@link RCallNode} in case there is a cached {@link RFunction} with cached varargs that
-     * exceeded the cache size {@link RCallNode#VARARGS_INLINE_CACHE_SIZE} .
-     *
-     * @see RCallNode
-     */
-    private static final class DispatchedGenericVarArgsCallNode extends VarArgsCacheCallNode {
-
-        @Child private DirectCallNode call;
-        @Child private CallArgumentsNode suppliedArgs;
-
-        @CompilationFinal private boolean needsCallerFrame;
-
-        DispatchedGenericVarArgsCallNode(RFunction function, CallArgumentsNode suppliedArgs, RCallNode originalCall) {
-            super(originalCall);
-            this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
-            this.suppliedArgs = suppliedArgs;
-        }
-
-        @Override
-        protected Object execute(VirtualFrame frame, RFunction currentFunction, ArgumentsSignature varArgsSignature, S3Args s3Args) {
-            CompilerDirectives.transferToInterpreter();
-
-            // Arguments may change every call: Flatt'n'Match on SlowPath! :-/
-            UnrolledVariadicArguments argsValuesAndNames = suppliedArgs.executeFlatten(frame);
-            MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(currentFunction, argsValuesAndNames, this, true);
-
-            if (!needsCallerFrame && currentFunction.containsDispatch()) {
+            if (argsNode == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                needsCallerFrame = true;
+                argsNode = insert(RArgumentsNode.create());
+                call = insert(Truffle.getRuntime().createDirectCallNode(cachedFunction.getTarget()));
+                if (needsSplitting(cachedFunction)) {
+                    call.cloneCallTarget();
+                }
             }
-            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
-            Object[] argsObject = RArguments.create(currentFunction, originalCall, callerFrame, RArguments.getDepth(frame) + 1, RArguments.getPromiseFrame(frame),
-                            matchedArgs.doExecuteArray(frame),
-                            matchedArgs.getSignature(), s3Args);
+            MaterializedFrame callerFrame = CompilerDirectives.inInterpreter() || originalCall.needsCallerFrame ? frame.materialize() : null;
+            Object[] argsObject = argsNode.execute(cachedFunction, originalCall.createCaller(frame, cachedFunction), callerFrame, RArguments.getDepth(frame) + 1, RArguments.getPromiseFrame(frame),
+                            orderedArguments, signature, s3Args);
             return call.call(frame, argsObject);
         }
     }
 
     @Override
     public RSyntaxElement getSyntaxLHS() {
-        return functionNode.asRSyntaxNode();
+        return getFunction() == null ? RSyntaxLookup.createDummyLookup(RSyntaxNode.LAZY_DEPARSE, "FUN", true) : getFunctionNode().asRSyntaxNode();
     }
 
     @Override
     public ArgumentsSignature getSyntaxSignature() {
-        return signature;
+        return signature == null ? ArgumentsSignature.empty(1) : signature;
     }
 
     @Override
     public RSyntaxElement[] getSyntaxArguments() {
-        return arguments.v;
+        return arguments == null ? new RSyntaxElement[]{RSyntaxLookup.createDummyLookup(RSyntaxNode.LAZY_DEPARSE, "...", false)} : arguments;
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
index 31af3be0e2ae22903e0b1a0d8b4f162c4e5fdce0..5053315ec963a72c311b422b48fbad04a65bef63 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
@@ -47,10 +47,10 @@ public class RCallerHelper {
     public static final class Representation implements RCaller {
 
         private final Object func;
-        private final Object[] arguments;
+        private final RArgsValuesAndNames arguments;
         private RSyntaxNode syntaxNode = null;
 
-        public Representation(Object func, Object[] arguments) {
+        public Representation(Object func, RArgsValuesAndNames arguments) {
             this.func = func;
             this.arguments = arguments;
         }
@@ -58,14 +58,14 @@ public class RCallerHelper {
         @Override
         public RSyntaxNode getSyntaxNode() {
             if (syntaxNode == null) {
-                RSyntaxNode[] syntaxArguments = new RSyntaxNode[arguments.length];
+                RSyntaxNode[] syntaxArguments = new RSyntaxNode[arguments.getLength()];
                 int index = 0;
                 // arguments are already ordered - once one is missing, all the remaining ones must
                 // be
                 // missing
                 boolean missing = false;
-                for (int i = 0; i < arguments.length; i++) {
-                    Object arg = arguments[i];
+                for (int i = 0; i < arguments.getLength(); i++) {
+                    Object arg = arguments.getArgument(i);
                     if (arg instanceof RPromise) {
                         assert !missing;
                         RPromise p = (RPromise) arg;
@@ -103,9 +103,6 @@ public class RCallerHelper {
                     }
 
                 }
-                // for some reason GNU R does not use argument names - hence empty signature even
-                // though
-                // an actual one is available
                 syntaxNode = RASTUtils.createCall(func, true, ArgumentsSignature.empty(syntaxArguments.length), syntaxArguments);
             }
             return syntaxNode;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
index b3e8ffc21b48ecd2dc46afc2169fbc8949448ab8..342c1c1a3d049fbe6c8321862e5b57364d4d36d8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
@@ -80,6 +80,14 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
         return new UseMethodFunctionLookupUninitializedNode(throwsError, nextMethod);
     }
 
+    public static S3FunctionLookupNode createWithError() {
+        return new UseMethodFunctionLookupUninitializedNode(true, false);
+    }
+
+    public static S3FunctionLookupNode createWithException() {
+        return new UseMethodFunctionLookupUninitializedNode(false, false);
+    }
+
     @FunctionalInterface
     private interface LookupOperation {
         Object read(MaterializedFrame frame, String name, boolean inMethodsTable);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java
index 3418749e522c45c5ca692713b441a6a619c5b949..56c0751202f6e38fef94f115bb4dcf895ce01efc 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/TemporarySlotNode.java
@@ -22,8 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.function;
 
-import java.util.function.Consumer;
-
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotTypeException;
@@ -34,16 +32,17 @@ import com.oracle.truffle.r.runtime.RInternalError;
 
 public final class TemporarySlotNode extends Node {
 
-    private static final Object[] defaultTempIdentifiers = new Object[]{new Object(), new Object(), new Object(), new Object()};
+    private static final Object[] defaultTempIdentifiers = new Object[]{new Object(), new Object(), new Object(), new Object(), new Object(), new Object(), new Object(), new Object()};
 
     @Child private FrameSlotNode tempSlot;
     private int tempIdentifier;
+    private Object identifier;
 
-    public FrameSlot initialize(VirtualFrame frame, Object value, Consumer<Object> initializer) {
+    public FrameSlot initialize(VirtualFrame frame, Object value, Runnable invalidate) {
         if (tempSlot == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            tempSlot = insert(FrameSlotNode.createInitialized(frame.getFrameDescriptor(), defaultTempIdentifiers[0], true));
-            initializer.accept(defaultTempIdentifiers[0]);
+            tempSlot = insert(FrameSlotNode.createInitialized(frame.getFrameDescriptor(), identifier = defaultTempIdentifiers[0], true));
+            invalidate.run();
         }
         FrameSlot slot = tempSlot.executeFrameSlot(frame);
         try {
@@ -52,9 +51,9 @@ public final class TemporarySlotNode extends Node {
                 // keep the complete loop in the slow path
                 do {
                     tempIdentifier++;
-                    Object identifier = tempIdentifier < defaultTempIdentifiers.length ? defaultTempIdentifiers[tempIdentifier] : new Object();
+                    identifier = tempIdentifier < defaultTempIdentifiers.length ? defaultTempIdentifiers[tempIdentifier] : new Object();
                     tempSlot.replace(FrameSlotNode.createInitialized(frame.getFrameDescriptor(), identifier, true));
-                    initializer.accept(identifier);
+                    invalidate.run();
                     slot = tempSlot.executeFrameSlot(frame);
                 } while (frame.isObject(slot) && frame.getObject(slot) != null);
             }
@@ -69,4 +68,8 @@ public final class TemporarySlotNode extends Node {
     public void cleanup(VirtualFrame frame, FrameSlot slot) {
         frame.setObject(slot, null);
     }
+
+    public Object getIdentifier() {
+        return identifier;
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
index 2258928a55dbd78a81de7667db9c34b96bae5b2e..70829ff60866dce7e8d25982da5008160c738003 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnmatchedArguments.java
@@ -28,7 +28,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 /**
  * An interface all arguments that are going to be matched need to implement.
  */
-interface UnmatchedArguments extends ClosureCache {
+public interface UnmatchedArguments extends ClosureCache {
     /**
      * @return The arguments to be matched. Individual {@link RNode}s may be <code>null</code> to
      *         denote "missingness"!
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnrolledVariadicArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnrolledVariadicArguments.java
index d2c1eb5b5ff02cf479765929f7a2f76e0e430eb9..b671bfe682c05ca423bce40b3f645ee5f3e11f76 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnrolledVariadicArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnrolledVariadicArguments.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.function;
 
 import java.util.IdentityHashMap;
 
-import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
@@ -32,7 +31,8 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 
 /**
  * This is a simple container class for arguments whose "..." have been unrolled and inserted into
- * the original arguments, as it happens in {@link CallArgumentsNode#executeFlatten(Frame)}.
+ * the original arguments, as it happens in
+ * {@link CallArgumentsNode#unrollArguments(ArgumentsSignature)}.
  */
 public final class UnrolledVariadicArguments extends Arguments<RNode> implements UnmatchedArguments {
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java
index 8990ac616929a540c5838c709b0ab1ece859d6f2..5210945655fb09e4166587e604b7aef188cbf9f4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RS4Object;
@@ -73,9 +72,6 @@ public abstract class WrapArgumentBaseNode extends RNode {
         if (result instanceof RVector) {
             everSeenVector.enter();
             return (RVector) result;
-        } else if (result instanceof RFactor) {
-            everSeenFactor.enter();
-            return ((RFactor) result).getVector();
         } else if (result instanceof RLanguage) {
             everSeenLanguage.enter();
             return (RLanguage) result;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
new file mode 100644
index 0000000000000000000000000000000000000000..2630a51f78a5d5e4a28cf2878fe0a7cdaaa1872c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2016, 2016, 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.function.call;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.RRootNode;
+import com.oracle.truffle.r.nodes.function.ArgumentMatcher;
+import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation;
+import com.oracle.truffle.r.nodes.function.CallArgumentsNode;
+import com.oracle.truffle.r.nodes.function.FormalArguments;
+import com.oracle.truffle.r.nodes.function.RCallNode;
+import com.oracle.truffle.r.nodes.function.UnmatchedArguments;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.nodes.RNode;
+
+/**
+ * This class provides the infrastructure to reorder the arguments based on R's argument matching
+ * rules. It implements two different paths: one for arguments provided as an
+ * {@link CallArgumentsNode}, i.e., unevaluated arguments, and another path for evaluated arguments.
+ */
+public abstract class PrepareArguments extends Node {
+
+    private static final int CACHE_SIZE = 4;
+
+    public abstract Object[] execute(VirtualFrame frame, RArgsValuesAndNames varArgs, RCallNode call);
+
+    public static PrepareArguments create(RFunction function, CallArgumentsNode args) {
+        return new UninitializedPrepareArguments(function, args);
+    }
+
+    public static PrepareArguments createExplicit(RFunction function) {
+        return new UninitializedExplicitPrepareArguments(function);
+    }
+
+    private static final class UninitializedPrepareArguments extends PrepareArguments {
+
+        private final RFunction function;
+        private final CallArgumentsNode sourceArguments; // not used as a node
+        private int depth = CACHE_SIZE;
+
+        UninitializedPrepareArguments(RFunction function, CallArgumentsNode sourceArguments) {
+            this.function = function;
+            this.sourceArguments = sourceArguments;
+        }
+
+        @Override
+        public Object[] execute(VirtualFrame frame, RArgsValuesAndNames varArgs, RCallNode call) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            PrepareArguments next;
+            if (depth-- > 0) {
+                next = new CachedPrepareArguments(this, function, call, sourceArguments, varArgs == null ? null : varArgs.getSignature());
+            } else {
+                next = new GenericPrepareArguments(function, sourceArguments);
+            }
+            return replace(next).execute(frame, varArgs, call);
+        }
+    }
+
+    private static final class CachedPrepareArguments extends PrepareArguments {
+
+        @Child private PrepareArguments next;
+        @Children private final RNode[] matchedArguments;
+        private final ArgumentsSignature cachedVarArgSignature;
+
+        CachedPrepareArguments(PrepareArguments next, RFunction function, RCallNode call, CallArgumentsNode args, ArgumentsSignature varArgSignature) {
+            this.next = next;
+            cachedVarArgSignature = varArgSignature;
+            matchedArguments = ArgumentMatcher.matchArguments(function, args.unrollArguments(varArgSignature), call, false);
+        }
+
+        @Override
+        @ExplodeLoop
+        public Object[] execute(VirtualFrame frame, RArgsValuesAndNames varArgs, RCallNode call) {
+            assert (cachedVarArgSignature != null) == (varArgs != null);
+            if (cachedVarArgSignature == null || cachedVarArgSignature == varArgs.getSignature()) {
+                Object[] result = new Object[matchedArguments.length];
+                for (int i = 0; i < matchedArguments.length; i++) {
+                    result[i] = matchedArguments[i].execute(frame);
+                }
+                return result;
+            }
+            return next.execute(frame, varArgs, call);
+        }
+    }
+
+    private static final class GenericPrepareArguments extends PrepareArguments {
+
+        private final RFunction function;
+        private final CallArgumentsNode args; // not used as a node
+
+        GenericPrepareArguments(RFunction function, CallArgumentsNode args) {
+            this.function = function;
+            this.args = args;
+        }
+
+        @Override
+        public Object[] execute(VirtualFrame frame, RArgsValuesAndNames varArgs, RCallNode call) {
+            CompilerDirectives.transferToInterpreter();
+            ArgumentsSignature varArgSignature = varArgs == null ? null : varArgs.getSignature();
+            UnmatchedArguments argsValuesAndNames = args.unrollArguments(varArgSignature);
+            RNode[] matchedArgs = ArgumentMatcher.matchArguments(function, argsValuesAndNames, RError.ROOTNODE, true);
+            Object[] result = new Object[matchedArgs.length];
+            for (int i = 0; i < matchedArgs.length; i++) {
+                result[i] = matchedArgs[i].execute(frame);
+            }
+            return result;
+        }
+    }
+
+    private static final class UninitializedExplicitPrepareArguments extends PrepareArguments {
+
+        private final RFunction function;
+        private int depth = CACHE_SIZE;
+
+        UninitializedExplicitPrepareArguments(RFunction function) {
+            this.function = function;
+        }
+
+        @Override
+        public Object[] execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, RCallNode call) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            PrepareArguments next;
+            if (depth-- > 0) {
+                next = new CachedExplicitPrepareArguments(this, function, call, explicitArgs == null ? null : explicitArgs.getSignature());
+            } else {
+                next = new GenericExplicitPrepareArguments(function);
+            }
+            return replace(next).execute(frame, explicitArgs, call);
+        }
+    }
+
+    private static final class CachedExplicitPrepareArguments extends PrepareArguments {
+
+        @Child private PrepareArguments next;
+
+        private final MatchPermutation permutation;
+        private final ArgumentsSignature cachedExplicitArgSignature;
+        private final FormalArguments formals;
+
+        CachedExplicitPrepareArguments(PrepareArguments next, RFunction function, RCallNode call, ArgumentsSignature explicitArgSignature) {
+            this.next = next;
+            formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
+            permutation = ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, false, function.getRBuiltin());
+            cachedExplicitArgSignature = explicitArgSignature;
+        }
+
+        @Override
+        public Object[] execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, RCallNode call) {
+            if (cachedExplicitArgSignature == explicitArgs.getSignature()) {
+                return ArgumentMatcher.matchArgumentsEvaluated(permutation, explicitArgs.getArguments(), formals);
+            }
+            return next.execute(frame, explicitArgs, call);
+        }
+    }
+
+    private static final class GenericExplicitPrepareArguments extends PrepareArguments {
+
+        private final RFunction function;
+
+        GenericExplicitPrepareArguments(RFunction function) {
+            this.function = function;
+        }
+
+        @Override
+        public Object[] execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, RCallNode call) {
+            CompilerDirectives.transferToInterpreter();
+            // Function and arguments may change every call: Flatt'n'Match on SlowPath! :-/
+            return ArgumentMatcher.matchArgumentsEvaluated(function, explicitArgs, RError.ROOTNODE, false).getArguments();
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java
index 0fe79bce163b58fcb88c4b303f7c59f484e94fec..2efb72901f8fec7b31d69558a44c0033c25d3b3a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java
@@ -27,13 +27,15 @@ import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.ArgumentStatePush;
 import com.oracle.truffle.r.nodes.function.PromiseNode;
-import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 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 com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 
 /**
  * Provides small helper function for eager evaluation of arguments for the use in
@@ -81,17 +83,17 @@ public class EagerEvalHelper {
         if (!optConsts()) {
             return null;
         }
-        if (expr instanceof RCallNode) {
-            RCallNode call = (RCallNode) expr;
-            if (call.getFunctionNode() instanceof ReadVariableNode) {
-                String functionName = ((ReadVariableNode) call.getFunctionNode()).getIdentifier();
+        if (expr instanceof RSyntaxCall) {
+            RSyntaxCall call = (RSyntaxCall) expr;
+            if (call.getSyntaxLHS() instanceof RSyntaxLookup) {
+                String functionName = ((RSyntaxLookup) call.getSyntaxLHS()).getIdentifier();
                 switch (functionName) {
                     case "character":
-                        if (call.getArgumentCount() == 0) {
+                        if (call.getSyntaxArguments().length == 0) {
                             return RDataFactory.createEmptyStringVector();
-                        } else if (call.getArgumentCount() == 1) {
-                            RSyntaxNode argument = call.getArgument(0);
-                            Integer value = ConstantNode.asIntConstant(argument, true);
+                        } else if (call.getSyntaxArguments().length == 1) {
+                            RSyntaxElement argument = call.getSyntaxArguments()[0];
+                            Integer value = RSyntaxConstant.asIntConstant(argument, true);
                             if (value != null) {
                                 RStringVector vector = RDataFactory.createStringVector(value);
                                 ArgumentStatePush.transitionStateSlowPath(vector);
@@ -101,8 +103,8 @@ public class EagerEvalHelper {
                         break;
                 }
             }
-        } else if (expr instanceof ConstantNode) {
-            return ((ConstantNode) expr).getValue();
+        } else if (expr instanceof RSyntaxConstant) {
+            return ((RSyntaxConstant) expr).getValue();
         }
         return null;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/InheritsCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/InheritsCheckNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..28a8070384beafcb8a5487ed8943365785b5fd0c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/InheritsCheckNode.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, 2016, 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.helpers;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
+import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
+import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.data.RMissing;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+
+/**
+ * Checks if given object has given R class. More specifically: whether its attribute class is a
+ * vector that contains given class name as an element.
+ */
+public class InheritsCheckNode extends Node {
+
+    @Child private ClassHierarchyNode classHierarchy = ClassHierarchyNodeGen.create(false, false);
+    private final ConditionProfile nullClassProfile = ConditionProfile.createBinaryProfile();
+    @CompilationFinal private ConditionProfile exactMatchProfile;
+    private final String checkedClazz;
+
+    public InheritsCheckNode(String checkedClazz) {
+        this.checkedClazz = checkedClazz;
+        assert RType.fromMode(checkedClazz) == null : "Class '" + checkedClazz + "' cannot be checked by InheritsCheckNode";
+    }
+
+    public boolean execute(Object value) {
+        if (value instanceof RMissing) {
+            return false;
+        }
+
+        RStringVector clazz = classHierarchy.execute(value);
+        if (nullClassProfile.profile(clazz != null)) {
+            for (int j = 0; j < clazz.getLength(); ++j) {
+                if (exactMatchProfile == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    exactMatchProfile = ConditionProfile.createBinaryProfile();
+                }
+                if (exactMatchProfile.profile(clazz.getDataAt(j) == checkedClazz) || clazz.getDataAt(j).equals(checkedClazz)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
new file mode 100644
index 0000000000000000000000000000000000000000..b05d17ea20f260ae2c35def3d0212bf3bb216958
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016, 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.helpers;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.AttributeAccess;
+import com.oracle.truffle.r.nodes.unary.CastStringNode;
+import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+
+/**
+ * Contains helper nodes related to factors, special R class of {@link RAbstractIntVector}. Note:
+ * there is also {@see IsFactorNode}, which implements a built-in, for checking factor class.
+ */
+public final class RFactorNodes {
+
+    private RFactorNodes() {
+    }
+
+    /**
+     * Encapsulates the operation of deciding whether a factor is ordered.
+     */
+    public static class GetOrdered extends Node {
+        @Child private AttributeAccess isOrderedAccess = AttributeAccess.create(RRuntime.ORDERED_ATTR_KEY);
+
+        public boolean execute(RAbstractIntVector factor) {
+            Object value = isOrderedAccess.execute(factor.getAttributes());
+            if (value instanceof RAbstractLogicalVector) {
+                RAbstractLogicalVector vec = (RAbstractLogicalVector) value;
+                return vec.getLength() > 0 && RRuntime.fromLogical(vec.getDataAt(0));
+            }
+
+            return false;
+        }
+    }
+
+    /**
+     * Encapsulates the operation of getting the 'levels' of a factor as a string vector.
+     */
+    public static class GetLevels extends Node {
+        @Child private CastStringNode castString;
+        @Child private AttributeAccess attrAccess = AttributeAccess.create(RRuntime.LEVELS_ATTR_KEY);
+
+        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        private final BranchProfile notVectorBranch = BranchProfile.create();
+        private final ConditionProfile nonScalarLevels = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile stringVectorLevels = ConditionProfile.createBinaryProfile();
+
+        /**
+         * Returns the levels as a string vector. If the 'levels' attribute is not a string vector a
+         * cast is done. May return null, if the 'levels' attribute is not present.
+         */
+        public RStringVector execute(RAbstractIntVector factor) {
+            Object attr = attrAccess.execute(factor.getAttributes());
+
+            // Convert scalars to vector if necessary
+            RVector vec;
+            if (nonScalarLevels.profile(attr instanceof RVector)) {
+                vec = (RVector) attr;
+            } else if (attr != null) {
+                vec = (RVector) RRuntime.asAbstractVector(attr);   // scalar to vector
+            } else {
+                notVectorBranch.enter();
+                // N.B: when a factor is lacking the 'levels' attribute, GNU R uses range 1:14331272
+                // as levels, but probably only in 'split'. Following example prints a huge list:
+                // { f <- factor(1:5); attr(f, 'levels') <- NULL; split(1:2, f) }
+                return null;
+            }
+
+            // Convert to string vector if necessary
+            if (stringVectorLevels.profile(vec instanceof RStringVector)) {
+                return (RStringVector) vec;
+            } else {
+                if (castString == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    castString = insert(CastStringNodeGen.create(false, false, false, false));
+                }
+                RStringVector slevels = (RStringVector) castString.executeString(vec);
+                return RDataFactory.createStringVector(slevels.getDataWithoutCopying(), RDataFactory.COMPLETE_VECTOR);
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
index 9225582669eeeb4ae0686c58c7ac2a14509cd16e..7b15cf7fede1479f5f3718de78a5bbf3983a8978 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
@@ -29,22 +29,8 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 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.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
 public abstract class CastIntegerNode extends CastIntegerBaseNode {
@@ -215,11 +201,6 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
         return ret;
     }
 
-    @Specialization
-    protected RIntVector doFactor(RFactor factor) {
-        return factor.getVector();
-    }
-
     // TODO Should be type-variable and moved to CastNode
     @Specialization(guards = {"args.getLength() == 1", "isIntVector(args.getArgument(0))"})
     protected RIntVector doRArgsValuesAndNames(RArgsValuesAndNames args) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java
index b60cb438a3b5d72dca159bb03cb19dd004c929bd..b41b5aeb679f14847f948eaf926d99c3fdd01aab 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java
@@ -27,17 +27,9 @@ import java.util.Arrays;
 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.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -79,7 +71,7 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
         return operand;
     }
 
-    @Specialization
+    @Specialization(guards = "!isFactor(operand)")
     protected RLogicalVector doIntVector(RAbstractIntVector operand) {
         return createResultVector(operand, index -> naCheck.convertIntToLogical(operand.getDataAt(index)));
     }
@@ -154,8 +146,8 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
         return missing;
     }
 
-    @Specialization
-    protected RLogicalVector asLogical(RFactor factor) {
+    @Specialization(guards = "isFactor(factor)")
+    protected RLogicalVector asLogical(RAbstractIntVector factor) {
         byte[] data = new byte[factor.getLength()];
         Arrays.fill(data, RRuntime.LOGICAL_NA);
         return RDataFactory.createLogicalVector(data, RDataFactory.INCOMPLETE_VECTOR);
@@ -170,4 +162,10 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
     public static CastLogicalNode createNonPreserving() {
         return CastLogicalNodeGen.create(false, false, false);
     }
+
+    @Child private InheritsCheckNode inheritsFactorCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
+
+    protected boolean isFactor(Object o) {
+        return inheritsFactorCheck.execute(o);
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java
index 260cca0912bf6659377cb15f0778bdabe54a7c11..c1a512ea7c8deab823d2a831203ecbe995e53750 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java
@@ -23,12 +23,7 @@
 package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
@@ -52,11 +47,6 @@ public abstract class CastToContainerNode extends CastBaseNode {
         return vector;
     }
 
-    @Specialization
-    protected RFactor cast(RFactor factor) {
-        return factor;
-    }
-
     @Specialization
     protected RExpression cast(RExpression expression) {
         return expression;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java
index 1734a58f8a5c0474be8b643ebb88c78427cd87c8..8f35c98a981be1d8b8318c449b2684fff67c227e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java
@@ -24,12 +24,7 @@ package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.api.dsl.NodeField;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 @NodeField(name = "nonVectorPreserved", type = boolean.class)
@@ -60,11 +55,6 @@ public abstract class CastToVectorNode extends CastNode {
         return vector;
     }
 
-    @Specialization
-    protected RAbstractVector cast(RFactor factor) {
-        return factor.getVector();
-    }
-
     @Specialization
     protected RList cast(RExpression expression) {
         return expression.getList();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/IsFactorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/IsFactorNode.java
index 74f2c2e330bd72da3ce0ff68f3c91c92cd4dad20..bf740255bc676013010b0c5148b838d1212ac733 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/IsFactorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/IsFactorNode.java
@@ -11,16 +11,19 @@
 
 package com.oracle.truffle.r.nodes.unary;
 
+import static com.oracle.truffle.r.runtime.RRuntime.LOGICAL_FALSE;
+import static com.oracle.truffle.r.runtime.RRuntime.LOGICAL_TRUE;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
 
 public abstract class IsFactorNode extends UnaryNode {
 
     @Child private TypeofNode typeofNode;
-    @Child private InheritsNode inheritsNode;
+    @Child private InheritsCheckNode inheritsCheck;
 
     public abstract byte executeIsFactor(Object c);
 
@@ -31,12 +34,15 @@ public abstract class IsFactorNode extends UnaryNode {
             typeofNode = insert(TypeofNodeGen.create());
         }
         if (typeofNode.execute(x) != RType.Integer) {
-            return RRuntime.LOGICAL_FALSE;
+            // Note: R does not allow to set class 'factor' to an arbitrary object, unlike with
+            // data.frame
+            return LOGICAL_FALSE;
         }
-        if (inheritsNode == null) {
+        if (inheritsCheck == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            inheritsNode = insert(InheritsNodeGen.create());
+            inheritsCheck = insert(new InheritsCheckNode(RRuntime.CLASS_FACTOR));
         }
-        return (byte) inheritsNode.executeObject(x, RDataFactory.createStringVector(RType.Factor.getName()), RRuntime.LOGICAL_FALSE);
+
+        return inheritsCheck.execute(x) ? LOGICAL_TRUE : LOGICAL_FALSE;
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
index 8afec0273e921dbcfce320b4deec79f0241990bc..8c7b2e531442fb5589ed6c5774535a926a3a51a2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
@@ -26,26 +26,7 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
@@ -187,11 +168,6 @@ public abstract class PrecedenceNode extends RBaseNode {
         return EXPRESSION_PRECEDENCE;
     }
 
-    @Specialization
-    protected int doFactor(RFactor val, byte recursive) {
-        return INT_PRECEDENCE;
-    }
-
     @Specialization
     protected int doS4Object(RS4Object o, byte recursive) {
         return LIST_PRECEDENCE;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index 598e597811d5c1bac3ed67fc399fb53f6702befd..093c75a0a7bb3d22d77c450bedb03e80695fb3d6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -80,12 +80,25 @@ public enum FastROptions {
 
     public boolean getBooleanValue() {
         assert isBoolean;
-        return (Boolean) value;
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        } else {
+            System.out.println("boolean option value expected with " + name() + " - forgot +/- ?");
+            System.exit(2);
+            return false;
+        }
+
     }
 
     public String getStringValue() {
         assert !isBoolean;
-        return (String) value;
+        if (value == null || value instanceof String) {
+            return (String) value;
+        } else {
+            System.out.println("string option value expected with " + name());
+            System.exit(2);
+            return "";
+        }
     }
 
     private static FastROptions[] VALUES = values();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index 4c52f4046f06685a7c1b5d115a6edf7722ccc236..c0c031affaa58c088ac0222e2abafa47b7c54c78 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -23,38 +23,14 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributes;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.REmpty;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-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.RPairList;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
+import com.oracle.truffle.r.runtime.nodes.*;
 
 /**
  * Deparsing R objects.
@@ -569,9 +545,6 @@ public class RDeparse {
             protected Void visit(RSyntaxConstant constant) {
                 // coerce scalar values to vectors and unwrap data frames and factors:
                 Object value = RRuntime.asAbstractVector(constant.getValue());
-                if (value instanceof RFactor) {
-                    value = ((RFactor) value).getVector();
-                }
 
                 if (value instanceof RExpression) {
                     append("expression(").appendListContents(((RExpression) value).getList()).append(')');
@@ -600,7 +573,7 @@ public class RDeparse {
                     append(')');
                 } else if (value instanceof RS4Object) {
                     RS4Object s4Obj = (RS4Object) value;
-                    Object clazz = s4Obj.getAttribute("class");
+                    Object clazz = s4Obj.getAttr("class");
                     String className = clazz == null ? "S4" : RRuntime.toString(RRuntime.asStringLengthOne(clazz));
                     append("new(\"").append(className).append('\"');
                     try (C c = indent()) {
@@ -926,8 +899,7 @@ public class RDeparse {
         }
 
         /**
-         * Handles {@link RList}, (@link RExpression}, and {@link RFactor}. Method name same as
-         * GnuR.
+         * Handles {@link RList}, (@link RExpression}. Method name same as GnuR.
          */
         private DeparseVisitor appendListContents(RAbstractListVector v) {
             int n = v.getLength();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
index bf8611503b131b00dbb305493ec1c6f30d1526a8..9d60e49c9b15a7b223b94bdba45840e14d96a7a0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java
@@ -128,7 +128,8 @@ public class RRuntime {
 
     public static final String DOT_S3_CLASS = ".S3Class";
     public static final String CLASS_DATA_FRAME = "data.frame";
-    public static final String CLASS_ORDERED = "ordered";
+    public static final String CLASS_FACTOR = "factor";
+    public static final String ORDERED_ATTR_KEY = "ordered";
 
     public static final String RS3MethodsTable = ".__S3MethodsTable__.";
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index 5206b2ada7985e774651c6545c4c10530850fa55..a45378ddf386a42d0d0b7b6b21a4e2a5de927450 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -11,21 +11,9 @@
  */
 package com.oracle.truffle.r.runtime;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
+import java.io.*;
 import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
+import java.util.*;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -34,37 +22,10 @@ import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RAttributes;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.REmpty;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.OptType;
 import com.oracle.truffle.r.runtime.data.RPromise.PromiseType;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RScalar;
-import com.oracle.truffle.r.runtime.data.RShareable;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.RUnboundValue;
-import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
@@ -1498,10 +1459,6 @@ public class RSerialize {
                             writeItem(RNull.instance);
                         }
                     }
-                } else if (type == SEXPTYPE.FASTR_FACTOR) {
-                    RFactor factor = (RFactor) obj;
-                    writeItem(factor.getVector());
-                    return;
                 } else {
                     // flags
                     RAttributes attributes = null;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RType.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RType.java
index 651c6f100aff2deafd0e947963144913b403fdbc..82816c766520b48e0bcdadc93231b55e693d8147 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RType.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RType.java
@@ -36,7 +36,6 @@ public enum RType {
     Closure("closure", -1),
     Builtin("builtin", -1),
     Special("special", -1),
-    Factor("factor", -1),
     Symbol("symbol", -1),
     Environment("environment", -1),
     PairList("pairlist", -1),
@@ -143,8 +142,6 @@ public enum RType {
                 return Builtin;
             case "special":
                 return Special;
-            case "factor":
-                return Factor;
             case "symbol":
                 return Symbol;
             case "environment":
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 9e9a027d826fd7149b75d41d8a76df84b2830d40..388ff08a9c6dd99bf2a3c95e00b11e14c681351a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -412,9 +412,10 @@ public final class Utils {
                     assert call != null;
                     RLanguage rl = RContext.getRRuntimeASTAccess().getSyntaxCaller(call);
                     RSyntaxNode sn = (RSyntaxNode) rl.getRep();
-                    SourceSection ss = sn.getSourceSection();
+                    SourceSection ss = sn != null ? sn.getSourceSection() : null;
                     // fabricate a srcref attribute from ss
-                    String path = ss.getSource().getPath();
+                    Source source = ss != null ? ss.getSource() : null;
+                    String path = source != null ? source.getPath() : null;
                     if (path != null && RInternalSourceDescriptions.isInternal(path)) {
                         path = null;
                     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java
index 609aef238392dc5b3b57b0678104620c299cdc3a..f5ed54d6b20c16c020e27db5d24cc9c4cdf3e1f3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java
@@ -23,7 +23,6 @@
 package com.oracle.truffle.r.runtime.data;
 
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 /**
@@ -33,6 +32,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
  * {@link RAttributable} is implemented by the {@link RAttributes} class.
  */
 public interface RAttributable extends RTypedValue {
+
     /**
      * If the attribute set is not initialized, then initialize it.
      *
@@ -49,16 +49,21 @@ public interface RAttributable extends RTypedValue {
     RAttributes getAttributes();
 
     /**
-     * Returns the value of the {@code class} attribute.
+     * Returns the value of the {@code class} attribute or empty {@link RStringVector} if class
+     * attribute is not set.
      */
-    RStringVector getClassHierarchy();
+    default RStringVector getClassHierarchy() {
+        Object v = getAttr(RRuntime.CLASS_ATTR_KEY);
+        RStringVector result = v instanceof RStringVector ? (RStringVector) v : getImplicitClass();
+        return result != null ? result : RDataFactory.createEmptyStringVector();
+    }
 
     /**
      * Returns {@code true} if the {@code class} attribute is set to {@link RStringVector} whose
      * first element equals to the given className.
      */
     default boolean hasClass(String className) {
-        RAbstractStringVector v = getClassHierarchy();
+        RStringVector v = getClassHierarchy();
         for (int i = 0; i < v.getLength(); ++i) {
             if (v.getDataAt(i).equals(className)) {
                 return true;
@@ -81,6 +86,14 @@ public interface RAttributable extends RTypedValue {
         }
     }
 
+    /**
+     * Get the value of an attribute. Returns {@code null} if not set.
+     */
+    default Object getAttr(String name) {
+        RAttributes attr = getAttributes();
+        return attr == null ? null : attr.get(name);
+    }
+
     /**
      * Set the attribute {@code name} to {@code value}, overwriting any existing value. This is
      * generic; a class may need to override this to handle certain attributes specially.
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributeStorage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributeStorage.java
index c8950954a78e352fca4739d8a680cdf5297ae29a..44cf397f6e5f2c585b25d3eebb4613ded89fba98 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributeStorage.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributeStorage.java
@@ -22,8 +22,6 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.r.runtime.RRuntime;
-
 /**
  * An adaptor class for the several R types that are attributable. Only useful for classes that
  * don't already inherit from another class, otherwise just cut and paste this code.
@@ -50,21 +48,6 @@ public abstract class RAttributeStorage extends RBaseObject implements RAttribut
         this.attributes = newAttributes;
     }
 
-    public final Object getAttribute(String name) {
-        RAttributes attr = attributes;
-        return attr == null ? null : attr.get(name);
-    }
-
     @Override
     public abstract RStringVector getImplicitClass();
-
-    @Override
-    public final RStringVector getClassHierarchy() {
-        RStringVector v = (RStringVector) getAttribute(RRuntime.CLASS_ATTR_KEY);
-        if (v == null) {
-            return getImplicitClass();
-        } else {
-            return v;
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index 51bb3a4873c4b5612c37e8fe9ceae54645b3d5c0..8cb3a253031143d295362945b3d4307940c9c566 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -356,10 +356,6 @@ public final class RDataFactory {
         return traceDataCreated(new RExpression(list));
     }
 
-    public static RFactor createFactor(RIntVector vector, boolean ordered) {
-        return traceDataCreated(new RFactor(vector, ordered));
-    }
-
     public static RSymbol createSymbol(String name) {
         assert name == name.intern();
         return traceDataCreated(new RSymbol(name));
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
index ba4747180593c6b1052530889c88cd5abf7eeb34..a365680d20d128c48ab93657ad3970982033a771 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
@@ -158,7 +158,7 @@ public class RExpression implements RShareable, RAbstractContainer {
 
     @Override
     public final RStringVector getClassHierarchy() {
-        RStringVector v = (RStringVector) data.getAttribute(RRuntime.CLASS_ATTR_KEY);
+        RStringVector v = (RStringVector) data.getAttr(RRuntime.CLASS_ATTR_KEY);
         if (v == null) {
             return getImplicitClass();
         } else {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
index 3e2ecabe1960185a9ec20b006063bb83a22c4ce6..367011fce4b174e190e0e3a7831d051fba891122 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java
@@ -23,237 +23,32 @@
 package com.oracle.truffle.r.runtime.data;
 
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-public final class RFactor implements RShareable, RAbstractContainer {
+public final class RFactor {
 
-    private final RIntVector vector;
-
-    private final boolean ordered;
-
-    public RFactor(RIntVector vector, boolean ordered) {
-        this.vector = vector;
-        this.ordered = ordered;
-    }
-
-    @Override
-    public int[] getInternalStore() {
-        return vector.getInternalStore();
-    }
-
-    @Override
-    public RType getRType() {
-        return RType.Integer;
-    }
-
-    public RIntVector getVector() {
-        return vector;
-    }
-
-    public boolean isOrdered() {
-        return ordered;
-    }
-
-    @Override
-    public boolean isComplete() {
-        return vector.isComplete();
-    }
-
-    @Override
-    public int getLength() {
-        return vector.getLength();
-    }
-
-    @Override
-    public RAbstractContainer resize(int size) {
-        return vector.resize(size);
-    }
-
-    @Override
-    public boolean isTemporary() {
-        return vector.isTemporary();
-    }
-
-    @Override
-    public boolean isShared() {
-        return vector.isShared();
-    }
-
-    @Override
-    public void incRefCount() {
-        vector.incRefCount();
-    }
-
-    @Override
-    public void decRefCount() {
-        vector.decRefCount();
-    }
-
-    @Override
-    public boolean isSharedPermanent() {
-        return vector.isSharedPermanent();
-    }
-
-    @Override
-    public void makeSharedPermanent() {
-        vector.makeSharedPermanent();
-    }
-
-    @Override
-    public RShareable getNonShared() {
-        RIntVector newVector = (RIntVector) vector.getNonShared();
-        return newVector == vector ? this : RDataFactory.createFactor(newVector, ordered);
-    }
-
-    @Override
-    public RFactor copy() {
-        return RDataFactory.createFactor((RIntVector) vector.copy(), ordered);
-    }
-
-    @Override
-    public RAttributes getAttributes() {
-        return vector.getAttributes();
-    }
-
-    @Override
-    public boolean hasDimensions() {
-        return vector.hasDimensions();
-    }
-
-    @Override
-    public int[] getDimensions() {
-        return vector.getDimensions();
-    }
-
-    @Override
-    public void setDimensions(int[] newDimensions) {
-        vector.setDimensions(newDimensions);
-    }
-
-    @Override
-    public Class<?> getElementClass() {
-        return RFactor.class;
-    }
-
-    @Override
-    public RFactor materializeNonShared() {
-        RVector v = vector.materializeNonShared();
-        return vector != v ? RDataFactory.createFactor((RIntVector) v, ordered) : this;
-    }
-
-    @Override
-    public Object getDataAtAsObject(int index) {
-        return vector.getDataAtAsObject(index);
-    }
-
-    @Override
-    public RStringVector getNames(RAttributeProfiles attrProfiles) {
-        return vector.getNames(attrProfiles);
-    }
-
-    @Override
-    public void setNames(RStringVector newNames) {
-        vector.setNames(newNames);
-    }
-
-    @Override
-    public RList getDimNames(RAttributeProfiles attrProfiles) {
-        return vector.getDimNames();
-    }
-
-    @Override
-    public void setDimNames(RList newDimNames) {
-        vector.setDimNames(newDimNames);
-    }
-
-    @Override
-    public Object getRowNames(RAttributeProfiles attrProfiles) {
-        return vector.getRowNames();
-    }
-
-    @Override
-    public void setRowNames(RAbstractVector rowNames) {
-        vector.setRowNames(rowNames);
-    }
-
-    @Override
-    public RStringVector getClassHierarchy() {
-        return vector.getClassHierarchy();
-    }
-
-    @Override
-    public RStringVector getImplicitClass() {
-        return vector.getImplicitClass();
-    }
-
-    @Override
-    public boolean isObject(RAttributeProfiles attrProfiles) {
-        return true;
-    }
-
-    @Override
-    public RAttributes initAttributes() {
-        return vector.initAttributes();
-    }
-
-    @Override
-    public void initAttributes(RAttributes newAttributes) {
-        vector.initAttributes(newAttributes);
-    }
-
-    @Override
-    public void setAttr(String name, Object value) {
-        vector.setAttr(name, value);
-    }
-
-    @Override
-    public Object getAttr(RAttributeProfiles attrProfiles, String name) {
-        return vector.getAttr(attrProfiles, name);
-    }
-
-    @Override
-    public RAttributes resetAllAttributes(boolean nullify) {
-        return vector.resetAllAttributes(nullify);
-    }
-
-    @Override
-    public RShareable materializeToShareable() {
-        return this;
-    }
-
-    public void setLevels(Object newLevels) {
-        vector.setAttr(RRuntime.LEVELS_ATTR_KEY, newLevels);
-    }
-
-    @Override
-    public RAbstractContainer setClassAttr(RStringVector classAttr, boolean convertToInt) {
-        return vector.setClassAttr(classAttr, convertToInt);
-    }
-
-    public RVector getLevels(RAttributeProfiles attrProfiles) {
-        Object attr = vector.getAttr(attrProfiles, RRuntime.LEVELS_ATTR_KEY);
-        if (attr instanceof RVector) {
-            return (RVector) attr;
-        } else {
-            // Scalar, must convert
-            return (RVector) RRuntime.asAbstractVector(attr);
-        }
+    private RFactor(RIntVector vector, boolean ordered) {
+        // only static helpers
     }
 
-    public int getNLevels(RAttributeProfiles attrProfiles) {
-        RVector levels = getLevels(attrProfiles);
-        return levels == null ? 0 : levels.getLength();
+    /**
+     * Helper method to get 'levels' of a factor. However, all the invocations of this method should
+     * be replaced with FactorNodes.GetLevel in the future.
+     */
+    public static RVector getLevels(RAbstractIntVector factor) {
+        return getLevelsImpl(factor.getAttr(RRuntime.LEVELS_ATTR_KEY));
     }
 
-    @Override
-    public int getGPBits() {
-        return vector.getGPBits();
+    /**
+     * Helper method to get 'levels' of a factor with profile. However, all the invocations of this
+     * method should be replaced with FactorNodes.GetLevel in the future.
+     */
+    public static RVector getLevels(RAttributeProfiles profile, RAbstractIntVector factor) {
+        return getLevelsImpl(factor.getAttr(profile, RRuntime.LEVELS_ATTR_KEY));
     }
 
-    @Override
-    public void setGPBits(int value) {
-        vector.setGPBits(value);
+    private static RVector getLevelsImpl(Object attr) {
+        // convert scalar to RVector if necessary
+        return attr instanceof RVector ? (RVector) attr : (RVector) RRuntime.asAbstractVector(attr);
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
index cfddfe74097a4c74a8d679bd809e96e0a7ee4181..e956c0faec14e3357cfe3b4e692b7e480a48ac40 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
@@ -484,10 +484,6 @@ public class RPromise implements RTypedValue {
         public RBaseNode getExpr() {
             return expr;
         }
-
-        public Closure deepCopy() {
-            return new Closure((RBaseNode) expr.deepCopy());
-        }
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java
index 790f1b2f2351db84be31a0675c2ede6a31201858..2aa99d23f1f6471b547c5c386006bb85cff8f46a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTypes.java
@@ -51,7 +51,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 @TypeSystem({boolean.class, byte.class, int.class, double.class, RRaw.class, RComplex.class, String.class, RIntSequence.class, RDoubleSequence.class, RIntVector.class, RDoubleVector.class,
                 RRawVector.class, RComplexVector.class, RStringVector.class, RLogicalVector.class, RFunction.class, RNull.class, RMissing.class, REmpty.class, REnvironment.class, RExpression.class,
                 RConnection.class, MaterializedFrame.class, FrameSlot.class, RAbstractIntVector.class, RAbstractDoubleVector.class, RAbstractLogicalVector.class, RAbstractComplexVector.class,
-                RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RFactor.class, RSymbol.class, RPromise.class, RLanguage.class,
+                RAbstractStringVector.class, RAbstractRawVector.class, RList.class, RAbstractVector.class, RSymbol.class, RPromise.class, RLanguage.class,
                 RPairList.class, RExternalPtr.class, RS4Object.class, RAbstractContainer.class, RAttributable.class, RTypedValue.class, RArgsValuesAndNames.class, RType.class, Object[].class})
 public class RTypes {
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index 4db5837712a5c7813680ca4d3beb54e810b37330..9292adf55f7794dcdcb8e2696eff364b578e5890 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -503,40 +503,32 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare
             // class attribute removed - no longer a data frame or factor (even if it was before)
             return vector;
         } else if (classAttr != null && classAttr.getLength() != 0) {
-            boolean ordered = false;
             for (int i = 0; i < classAttr.getLength(); i++) {
                 String attr = classAttr.getDataAt(i);
-                if (RRuntime.CLASS_ORDERED.equals(attr)) {
-                    // "ordered" must be specified before "factor" - hence it's enough to do the
-                    // check only before encountering the "factor"
-                    ordered = true;
-                }
-                if (RType.Factor.getName().equals(attr)) {
+                if (RRuntime.CLASS_FACTOR.equals(attr)) {
+                    // For Factors we only have to check if the data-type is Integer, because
+                    // otherwise we must show error.
+                    // Note: this can only happen if the class is set by hand to some non-integral
+                    // vector, i.e. attr(doubles, 'class') <- 'factor'
                     vector.putAttribute(RRuntime.CLASS_ATTR_KEY, classAttr);
-                    if (enclosingFactor != null) {
-                        // was a factor and still is a factor
-                        return enclosingFactor;
-                    } else {
-                        RIntVector resVector;
-                        if (vector.getElementClass() != RInteger.class) {
-                            if (vector.getElementClass() == RDouble.class && convertToInt) {
-                                RDoubleVector sourceVector = (RDoubleVector) vector;
-                                int[] data = new int[sourceVector.getLength()];
-                                for (int j = 0; j < data.length; j++) {
-                                    data[j] = RRuntime.double2int(sourceVector.getDataAt(j));
-                                }
-                                resVector = RDataFactory.createIntVector(data, sourceVector.isComplete());
-                                resVector.copyAttributesFrom(sourceVector);
-                            } else {
-                                // TODO: add invoking node
-                                throw RError.error(RError.SHOW_CALLER2, RError.Message.ADDING_INVALID_CLASS, "factor");
+                    if (vector.getElementClass() != RInteger.class) {
+                        // TODO: check when this 'convertToInt' is necessary
+                        if (vector.getElementClass() == RDouble.class && convertToInt) {
+                            RDoubleVector sourceVector = (RDoubleVector) vector;
+                            int[] data = new int[sourceVector.getLength()];
+                            for (int j = 0; j < data.length; j++) {
+                                data[j] = RRuntime.double2int(sourceVector.getDataAt(j));
                             }
+                            RIntVector resVector = RDataFactory.createIntVector(data, sourceVector.isComplete());
+                            resVector.copyAttributesFrom(sourceVector);
+                            return resVector;
                         } else {
-                            resVector = (RIntVector) vector;
+                            // TODO: add invoking node
+                            throw RError.error(RError.SHOW_CALLER2, RError.Message.ADDING_INVALID_CLASS, "factor");
                         }
-                        // it's a factor now
-                        return RDataFactory.createFactor(resVector, ordered);
                     }
+
+                    break;  // TODO: check if setting connection and then factor shows both errors
                 } else if (RType.Connection.getName().equals(attr)) {
                     // convert to RConnection
                     return ConnectionSupport.fromVector(vector, classAttr);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
index 128abb04ab71e9b131a383763ca70dc776cce073..1907afe018d8f94171746d739935f3fb60c38a7e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
@@ -22,22 +22,8 @@
  */
 package com.oracle.truffle.r.runtime.data.closures;
 
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RInteger;
-import com.oracle.truffle.r.runtime.data.RLogical;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RString;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 
 public class RClosures {
 
@@ -119,8 +105,11 @@ public class RClosures {
 
     // Factor to vector
 
-    public static RAbstractVector createFactorToVector(RFactor factor, boolean withNames, RAttributeProfiles attrProfiles) {
-        RAbstractVector levels = factor.getLevels(attrProfiles);
+    public static RAbstractVector createFactorToVector(RAbstractIntVector factor, boolean withNames, RAttributeProfiles attrProfiles) {
+        return createFactorToVector(factor, withNames, RFactor.getLevels(attrProfiles, factor));
+    }
+
+    public static RAbstractVector createFactorToVector(RAbstractIntVector factor, boolean withNames, RVector levels) {
         if (levels == null) {
             return new RFactorToStringVectorClosure(factor, null, withNames);
         } else {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToComplexVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToComplexVectorClosure.java
index 0ecce378181f1a44c1c00f818265e09824340300..680e2c5aebb22a6a30704d1d8b9349131b7d6187 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToComplexVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToComplexVectorClosure.java
@@ -27,10 +27,10 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /*
@@ -41,8 +41,8 @@ final class RFactorToComplexVectorClosure extends RToComplexVectorClosure implem
     private final RAbstractComplexVector levels;
     private final boolean withNames;
 
-    RFactorToComplexVectorClosure(RFactor factor, RAbstractComplexVector levels, boolean withNames) {
-        super(factor.getVector());
+    RFactorToComplexVectorClosure(RAbstractIntVector vector, RAbstractComplexVector levels, boolean withNames) {
+        super(vector);
         assert levels != null;
         this.levels = levels;
         this.withNames = withNames;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToDoubleVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToDoubleVectorClosure.java
index 6fac21bc3c61549c2174db272d847edc5b2e7c6e..45b41c2cfe2dc83860e11c908e1da96a3660f2ee 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToDoubleVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToDoubleVectorClosure.java
@@ -26,10 +26,10 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /*
@@ -40,8 +40,8 @@ final class RFactorToDoubleVectorClosure extends RToDoubleVectorClosure implemen
     private final RAbstractDoubleVector levels;
     private final boolean withNames;
 
-    RFactorToDoubleVectorClosure(RFactor factor, RAbstractDoubleVector levels, boolean withNames) {
-        super(factor.getVector());
+    RFactorToDoubleVectorClosure(RAbstractIntVector vector, RAbstractDoubleVector levels, boolean withNames) {
+        super(vector);
         assert levels != null;
         this.levels = levels;
         this.withNames = withNames;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToIntVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToIntVectorClosure.java
index 39bb9913ef5b2cea6d9ac4d65ed5a6500131241e..8e09817efcc8de283347149733a2c36effd09be5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToIntVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToIntVectorClosure.java
@@ -26,7 +26,6 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -40,8 +39,8 @@ final class RFactorToIntVectorClosure extends RToIntVectorClosure implements RAb
     private final RAbstractIntVector levels;
     private final boolean withNames;
 
-    RFactorToIntVectorClosure(RFactor factor, RAbstractIntVector levels, boolean withNames) {
-        super(factor.getVector());
+    RFactorToIntVectorClosure(RAbstractIntVector vector, RAbstractIntVector levels, boolean withNames) {
+        super(vector);
         assert levels != null;
         this.levels = levels;
         this.withNames = withNames;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java
index 5581ce61bb2fe5fb469f0e3ba7b140cb4cf35ba6..d6223712ab2613a0364bbab38612b35aced8b340 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RFactorToStringVectorClosure.java
@@ -27,9 +27,9 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RFactor;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
@@ -41,8 +41,8 @@ public final class RFactorToStringVectorClosure extends RToStringVectorClosure i
     private final RAbstractStringVector levels;
     private final boolean withNames;
 
-    RFactorToStringVectorClosure(RFactor factor, RAbstractStringVector levels, boolean withNames) {
-        super(factor.getVector());
+    RFactorToStringVectorClosure(RAbstractIntVector vector, RAbstractStringVector levels, boolean withNames) {
+        super(vector);
         this.levels = levels;
         this.withNames = withNames;
         if (this.levels == null) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
index 9701130c96eb4f3af92341efb5c37c7f1fc47edf..7dc89eb98a5ebc01e718a2afd693acab71a7bb26 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
@@ -17,32 +17,8 @@ import java.util.Map;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.REmpty;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RPromise;
+import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RUnboundValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 // Transcribed from GnuR src/include/Rinternals.h and src/main/serialize.c
@@ -57,8 +33,6 @@ public enum SEXPTYPE {
     FASTR_INT(301, Integer.class),
     FASTR_BYTE(302, Byte.class),
     FASTR_COMPLEX(303, RComplex.class),
-    // FastR special "vector" types
-    FASTR_FACTOR(305, RFactor.class),
     // very special case
     FASTR_SOURCESECTION(306, SourceSection.class),
     FASTR_CONNECTION(307, RConnection.class),
@@ -196,8 +170,6 @@ public enum SEXPTYPE {
                 return SEXPTYPE.LGLSXP;
             case FASTR_COMPLEX:
                 return SEXPTYPE.CPLXSXP;
-            case FASTR_FACTOR:
-                return SEXPTYPE.VECSXP;
             case FASTR_CONNECTION:
                 return SEXPTYPE.INTSXP;
             default:
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java
index 06dc66ae1cdda10bee55c1c23cecdbda6d4470db..e4bcc042eaa484f81539bab75c9e38729876ffc7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RNode.java
@@ -33,39 +33,8 @@ import com.oracle.truffle.api.nodes.UnexpectedResultException;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributes;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RFactor;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RTypes;
-import com.oracle.truffle.r.runtime.data.RTypesGen;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 @TypeSystemReference(RTypes.class)
@@ -193,10 +162,6 @@ public abstract class RNode extends RBaseNode implements RInstrumentableNode {
         return RTypesGen.expectRExpression(execute(frame));
     }
 
-    public RFactor executeRFactor(VirtualFrame frame) throws UnexpectedResultException {
-        return RTypesGen.expectRFactor(execute(frame));
-    }
-
     public RSymbol executeRSymbol(VirtualFrame frame) throws UnexpectedResultException {
         return RTypesGen.expectRSymbol(execute(frame));
     }
@@ -285,10 +250,6 @@ public abstract class RNode extends RBaseNode implements RInstrumentableNode {
         return value instanceof RList;
     }
 
-    protected static boolean isRFactor(Object value) {
-        return value instanceof RFactor;
-    }
-
     protected static boolean isRPromise(Object value) {
         return value instanceof RPromise;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxConstant.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxConstant.java
index 6e8fabfb0db65285a65a4ace262d13d83fc101cb..40a233293549515696f76dcbe0af3ac8c319486c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxConstant.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxConstant.java
@@ -53,4 +53,19 @@ public interface RSyntaxConstant extends RSyntaxElement {
             }
         };
     }
+
+    /**
+     * Helper function: extract an integer constant from an RSyntaxElement.
+     */
+    static Integer asIntConstant(RSyntaxElement argument, boolean castFromDouble) {
+        if (argument instanceof RSyntaxConstant) {
+            Object value = ((RSyntaxConstant) argument).getValue();
+            if (value instanceof Integer) {
+                return (int) value;
+            } else if (castFromDouble && value instanceof Double) {
+                return (int) (double) value;
+            }
+        }
+        return null;
+    }
 }
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 75c0f933adec641cd1ac465feed827e92279f7f5..9804e42bacde3c0ce7462627471866f6b82744e7 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
@@ -23552,6 +23552,15 @@ Error: unexpected symbol in " ', NA), mag = c('Min.   :5.000  ', '1st Qu.:5.300
 #argv <- list(structure(list(Topic = c('myTst-package', 'foo-class', 'myTst', 'show,foo-method', 'show,foo-method', 'show-methods'), File = c('myTst-package', 'foo-class', 'myTst-package', 'foo-class', 'show-methods', 'show-methods')), .Names = c('Topic', 'File'), row.names = c(3L, 1L, 4L, 2L, 6L, 5L), class = 'data.frame'));is.matrix(argv[[1]]);
 [1] FALSE
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_isna.testIsNA
+#is.na(data.frame(col1=1:5, col2=c(NA, 1, NA, 2, NA)))
+      col1  col2
+[1,] FALSE  TRUE
+[2,] FALSE FALSE
+[3,] FALSE  TRUE
+[4,] FALSE FALSE
+[5,] FALSE  TRUE
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_isna.testIsNA
 #is.na(is.na))
 Error: unexpected ')' in "is.na(is.na))"
@@ -31534,6 +31543,25 @@ NULL
 [1] "some"
 [1] "test"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testDispatchToOpsSpecializations
+#data.frame(factor(c(1,2,1))) == data.frame(factor(c(1,2,2)))
+     factor.c.1..2..1..
+[1,]               TRUE
+[2,]               TRUE
+[3,]              FALSE
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testDispatchToOpsSpecializations
+#data.frame(factor(c(1,2,3))) == data.frame(factor(c(1,2,3)))
+     factor.c.1..2..3..
+[1,]               TRUE
+[2,]               TRUE
+[3,]               TRUE
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testDispatchToOpsSpecializations
+#factor(c("a","b","c")) == factor(c(1,2,3))
+Error in Ops.factor(factor(c("a", "b", "c")), factor(c(1, 2, 3))) :
+  level sets of factors are different
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testIn
 #{ "hello" %in% c("I", "say", "hello", "world") }
 [1] TRUE
@@ -36487,6 +36515,10 @@ Error in parent.frame(0) : invalid 'n' value
 [1] 3
 [1] 0
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_parse.test
+#{ parse(text="NULL") }
+expression(NULL)
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_parse.test
 #{ typeof(parse(text = "foo", keep.source = FALSE, srcfile = NULL)[[1]]) }
 [1] "symbol"
@@ -52941,6 +52973,10 @@ Error in setwd(1) : character argument expected
 #{ setwd(character()) }
 Error in setwd(character()) : character argument expected
 
+##com.oracle.truffle.r.test.functions.TestFunctions.testArgEvaluationOrder
+#v <- 1; class(v) <- 'foo'; `-.foo` <- function(x,y,...) { cat('[-.foo]'); 1234 }; g <- function(...) { cat('[ing]'); ({cat('[-]'); `-`})(...) }; ({cat('[g]'); g})({cat('[x]'); v},{cat('[y]'); 3},{cat('[z]'); 5})
+[g][ing][-][x][y][z][-.foo][1] 1234
+
 ##com.oracle.truffle.r.test.functions.TestFunctions.testArgs
 #{ f<-function(x, row.names = NULL, optional = FALSE, ...) {print(optional); for (i in list(...)) {print(i)} }; f(c(7,42), row.names=NULL, nm="x") }
 [1] FALSE
@@ -53251,6 +53287,10 @@ a
 ##com.oracle.truffle.r.test.functions.TestFunctions.testDots
 #f2 <- function(...) { f <- function() cat(...); f() }; f2()
 
+##com.oracle.truffle.r.test.functions.TestFunctions.testDots
+#{ `-.foo` <- function(...) 123; v <- 1; class(v) <- 'foo'; sapply(1,`-`,v); sapply(v,`-`,1); sapply(v,`-`,v) }
+[1] 123
+
 ##com.oracle.truffle.r.test.functions.TestFunctions.testDots
 #{ asdf <- function(x,...) UseMethod("asdf",x); asdf.numeric <- function(x, ...) print(paste("num:", x, ...)); asdf(1) }
 [1] "num: 1"
@@ -106314,6 +106354,110 @@ debug at #4: t
 exiting from: f(5)
 [1] 6
 
+##com.oracle.truffle.r.test.library.utils.TestTrace.testCondTrace
+#f <- function(x) {}; (if (exists('.fastr.trace')) .fastr.trace else trace)(f, tracer=quote(if (x == 3 || x == 7) print(x))); g <- function() for (i in 1:10) f(i); g()
+[1] "f"
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+[1] 3
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+[1] 7
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testCondTrace
+#f <- function(x) {}; trace(f, tracer=quote(if (x == 3 || x == 7) print(x))); g <- function() for (i in 1:10) f(i); g()
+[1] "f"
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+[1] 3
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+[1] 7
+Tracing f(i) on entry
+Tracing f(i) on entry
+Tracing f(i) on entry
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testMultiTrace
+#f <- function(x) {}; (if (exists('.fastr.trace')) .fastr.trace else trace)(f, tracer=quote(print(x))); g <- function() for (i in 1:10) f(i); g()
+[1] "f"
+Tracing f(i) on entry
+[1] 1
+Tracing f(i) on entry
+[1] 2
+Tracing f(i) on entry
+[1] 3
+Tracing f(i) on entry
+[1] 4
+Tracing f(i) on entry
+[1] 5
+Tracing f(i) on entry
+[1] 6
+Tracing f(i) on entry
+[1] 7
+Tracing f(i) on entry
+[1] 8
+Tracing f(i) on entry
+[1] 9
+Tracing f(i) on entry
+[1] 10
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testMultiTrace
+#f <- function(x) {}; trace(f, tracer=quote(print(x))); g <- function() for (i in 1:10) f(i); g()
+[1] "f"
+Tracing f(i) on entry
+[1] 1
+Tracing f(i) on entry
+[1] 2
+Tracing f(i) on entry
+[1] 3
+Tracing f(i) on entry
+[1] 4
+Tracing f(i) on entry
+[1] 5
+Tracing f(i) on entry
+[1] 6
+Tracing f(i) on entry
+[1] 7
+Tracing f(i) on entry
+[1] 8
+Tracing f(i) on entry
+[1] 9
+Tracing f(i) on entry
+[1] 10
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testSimple
+#f <- function(x) {}; (if (exists('.fastr.trace')) .fastr.trace else trace)(f); f()
+trace: f()
+NULL
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testSimple
+#f <- function(x) {}; trace(f); f()
+trace: f()
+NULL
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testSimpleTrace
+#f <- function(x) {}; (if (exists('.fastr.trace')) .fastr.trace else trace)(f, tracer=quote(print(x))); f(100)
+[1] "f"
+Tracing f(100) on entry
+[1] 100
+NULL
+
+##com.oracle.truffle.r.test.library.utils.TestTrace.testSimpleTrace
+#f <- function(x) {}; trace(f, tracer=quote(print(x))); f(100)
+[1] "f"
+Tracing f(100) on entry
+[1] 100
+NULL
+
 ##com.oracle.truffle.r.test.library.utils.TestUtils.testHeadNTail
 #{head(letters)}
 [1] "a" "b" "c" "d" "e" "f"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java
index 1d32d7b884107d3adcefbdc4b1df28aa8f96f80e..610e8a0c3c45cf3185af28ae89f100440b8b68bd 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java
@@ -265,5 +265,8 @@ public class TestBuiltin_isna extends TestBase {
         assertEval("{ is.na(list(1[10],1L[10],list(),integer())) }");
         assertEval(Output.ContainsWarning, "is.na(quote(x()))");
         assertEval("is.na(is.na))");
+
+        // Note: is.na.data.frame calls do.call("cbind", lapply(x, "is.na")) - there is the error
+        assertEval(Ignored.Unimplemented, "is.na(data.frame(col1=1:5, col2=c(NA, 1, NA, 2, NA)))");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java
index 147b62182ec86227055a1a6a917e21cc90e868b9..134587376b167655ccbc67329bd787200dec691b 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java
@@ -1921,6 +1921,13 @@ public class TestBuiltin_operators extends TestBase {
         assertEval("argv <- list(181L, 3.14159265358979);`*`(argv[[1]],argv[[2]]);");
     }
 
+    @Test
+    public void testDispatchToOpsSpecializations() {
+        assertEval(Output.ContainsError, "factor(c(\"a\",\"b\",\"c\")) == factor(c(1,2,3))");
+        assertEval("data.frame(factor(c(1,2,3))) == data.frame(factor(c(1,2,3)))");
+        assertEval("data.frame(factor(c(1,2,1))) == data.frame(factor(c(1,2,2)))");
+    }
+
     @Test
     public void testOperators() {
         assertEval("{ `+`(1,2) }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_parse.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_parse.java
index d67f096cf0c946c5ecab7b2c5fa8d543540f3a43..ea5123f3a742966ef0b15af583359a9131da2810 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_parse.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_parse.java
@@ -41,5 +41,6 @@ public class TestBuiltin_parse extends TestBase {
     @Test
     public void test() {
         assertEval("{ typeof(parse(text = \"foo\", keep.source = FALSE, srcfile = NULL)[[1]]) }");
+        assertEval("{ parse(text=\"NULL\") }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R
index 6f256ba1edc098ad5cc5df234914db9744c0e4cb..3dad930c90bf637dbee58fa8b95ab791bab93b1b 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R
@@ -1,15 +1,15 @@
 # test remote update of a list containing atomic vector (must stay private)
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1][1]<-7; fastr.channel.send(ch, x)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); x[1][1]<-7; .fastr.channel.send(ch, x)"
+    .fastr.context.spawn(cx, code)
     y<-list(c(42))
-    fastr.channel.send(ch, y)
+    .fastr.channel.send(ch, y)
     x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(c(x,y))
 } else {
     print(list(7L, 42L))
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R
index 895a598caba26bccfebf63a324eb89771daf1130..21066855d7154826ea64e20c0415466bb9f32b05 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R
@@ -1,15 +1,15 @@
 # test sending a function
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); f<-fastr.channel.receive(ch); x<-f(7); fastr.channel.send(ch, x)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); f<-.fastr.channel.receive(ch); x<-f(7); .fastr.channel.send(ch, x)"
+    .fastr.context.spawn(cx, code)
     mul<-function(y) y*y
-    fastr.channel.send(ch, mul)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    .fastr.channel.send(ch, mul)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(x)
 } else {
     print(49)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R
index 91279d5a4196b5a0652a0ec6bb1a1666df720faa..fad902306100ac9fea6f57263d74fc1cfe58e0a1 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R
@@ -1,14 +1,14 @@
 # test sending global environment and assigning it remotely (should assign remotely but not locally)
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); env<-fastr.channel.receive(ch); assign('y', 7, pos=env); fastr.channel.send(ch, y)"
-    fastr.context.spawn(cx, code)
-    fastr.channel.send(ch, .GlobalEnv)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); env<-.fastr.channel.receive(ch); assign('y', 7, pos=env); .fastr.channel.send(ch, y)"
+    .fastr.context.spawn(cx, code)
+    .fastr.channel.send(ch, .GlobalEnv)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(list(x, exists('y')))
 } else {
     print(list(7, FALSE))
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R
index 668e4c70eacb13a8a6275b220368e76f610692df..a176be9ef11ab09bdc47fbb7b839fdca257c44f8 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R
@@ -1,16 +1,16 @@
 # test sending global environment as an attribute and assigning it remotely (should assign remotely but not locally)
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); msg<-fastr.channel.receive(ch); env<-attr(msg, 'GLOBAL'); assign('y', 7, pos=env); fastr.channel.send(ch, y)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); msg<-.fastr.channel.receive(ch); env<-attr(msg, 'GLOBAL'); assign('y', 7, pos=env); .fastr.channel.send(ch, y)"
+    .fastr.context.spawn(cx, code)
     l<-list(c(42))
     attr(l, 'GLOBAL')<-.GlobalEnv
-    fastr.channel.send(ch, l)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    .fastr.channel.send(ch, l)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(list(x, exists('y')))
 } else {
     print(list(7, FALSE))
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R
index 7f22a1c85ebf803fabafd657b3ce55d2a889ef55..c2994bddfd5356a1d04ee3a6bb6704b3e0a6b0e7 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R
@@ -1,20 +1,20 @@
 # test sending global environment as a nested attribute (an attribute of a list which is an attribute
 # of another list) and assigning it remotely (should assign remotely but not locally)
- 
+
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); msg<-fastr.channel.receive(ch); env<-attr(attr(msg, 'LIST'), 'GLOBAL'); assign('y', 7, pos=env); fastr.channel.send(ch, y)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); msg<-.fastr.channel.receive(ch); env<-attr(attr(msg, 'LIST'), 'GLOBAL'); assign('y', 7, pos=env); .fastr.channel.send(ch, y)"
+    .fastr.context.spawn(cx, code)
     l2<-list(c(42))
     l<-list(c(7))
     attr(l, 'GLOBAL')<-.GlobalEnv
     attr(l2, 'LIST')<-l
-    fastr.channel.send(ch, l2)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    .fastr.channel.send(ch, l2)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(list(x, exists('y')))
 } else {
     print(list(7, FALSE))
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R
index 51569a1cd887cd0da946160879a6b23d2a3b4941..216630c64a80dc10a78abc7bd139db043ae0e819 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R
@@ -1,16 +1,16 @@
 # test remote vector transfer for read - should use the same vector
- 
+
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); y<-x[1]; z<-fastr.identity(x); fastr.channel.send(ch, z)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); y<-x[1]; z<-.fastr.identity(x); .fastr.channel.send(ch, z)"
+    .fastr.context.spawn(cx, code)
     y<-c(7, 42)
-    z<-fastr.identity(y)
-    fastr.channel.send(ch, y)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    z<-.fastr.identity(y)
+    .fastr.channel.send(ch, y)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(x == z)
 } else {
     print(TRUE)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R
index 36460b93c8973f1224521b963b93faa88a50e5e0..f8982e2fc8ac27eb84567edde0b22152312932fe 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R
@@ -1,16 +1,16 @@
 # test remote list transfer for read - should use the same vector elements
- 
+
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); y<-x[[1]][1]; z<-fastr.identity(x[[1]]); fastr.channel.send(ch, z)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); y<-x[[1]][1]; z<-.fastr.identity(x[[1]]); .fastr.channel.send(ch, z)"
+    .fastr.context.spawn(cx, code)
     y<-list(c(7, 42), 1)
-    z<-fastr.identity(y[[1]])
-    fastr.channel.send(ch, y)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    z<-.fastr.identity(y[[1]])
+    .fastr.channel.send(ch, y)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(x == z)
 } else {
     print(TRUE)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R
index c4337f2bbcb4cb266de222ce45edb9e8c46ee495..d15979ec4ca5b8519b0ec1a2bf84041bf7c96b55 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R
@@ -1,18 +1,18 @@
 # test remote list transfer for read - should use the same vector elements despite non-sharable content
 # also in the list
- 
+
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
-    ch <- fastr.channel.create(1L)
-    cx <- fastr.context.create("SHARED_NOTHING")
-    code <- "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); y<-x[[1]][1]; z<-c(fastr.identity(x[[1]]), fastr.identity(x[[2]])) ; fastr.channel.send(ch, z)"
-    fastr.context.spawn(cx, code)
+    ch <- .fastr.channel.create(1L)
+    cx <- .fastr.context.create("SHARED_NOTHING")
+    code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); y<-x[[1]][1]; z<-c(.fastr.identity(x[[1]]), .fastr.identity(x[[2]])) ; .fastr.channel.send(ch, z)"
+    .fastr.context.spawn(cx, code)
     y<-list(c(7, 42), function(x) 1)
-    z<-fastr.identity(y[[1]])
-    w<-fastr.identity(y[[2]])
-    fastr.channel.send(ch, y)
-    x<-fastr.channel.receive(ch)
-    fastr.context.join(cx)
-    fastr.channel.close(ch)
+    z<-.fastr.identity(y[[1]])
+    w<-.fastr.identity(y[[2]])
+    .fastr.channel.send(ch, y)
+    x<-.fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
     print(c(x[1] == z, x[2] == w))
 } else {
     print(c(TRUE, FALSE))
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java
index f5e7184962b27130766598232c77023bef39f779..5d0a4c7bf52c1e6c7748fb3f5a8a7ad7d5b0d177 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java
@@ -217,6 +217,11 @@ public class TestFunctions extends TestBase {
                         "e2 <- new.env(); e2n <- new.env(); assign(\"b\", \"isb\", e2n); assign(\"ex\", e2n, e2); ex1 <- c(\"a\"); ex2 <- c(\"b\"); f(e1, ex1); f(e2, ex2) == \"isb\"");
     }
 
+    @Test
+    public void testArgEvaluationOrder() {
+        assertEval("v <- 1; class(v) <- 'foo'; `-.foo` <- function(x,y,...) { cat('[-.foo]'); 1234 }; g <- function(...) { cat('[ing]'); ({cat('[-]'); `-`})(...) }; ({cat('[g]'); g})({cat('[x]'); v},{cat('[y]'); 3},{cat('[z]'); 5})");
+    }
+
     @Test
     public void testMatching() {
         assertEval("{ x<-function(foo,bar){foo*bar} ; x(f=10,2) }");
@@ -311,7 +316,7 @@ public class TestFunctions extends TestBase {
         assertEval("f2 <- function(...) { f <- function() cat(...); f() }; f2()");
         assertEval("f2 <- function(...) { f <- function() cat(...); environment(f) <- globalenv(); f() }; f2()");
         assertEval("f2 <- function(...) { f <- function() cat(...); f() }; f2(\"a\")");
-        assertEval("f2 <- function(...) { f <- function() cat(...); assign(\"...\", NULL); f() }; f2(\"a\")");
+        assertEval(Ignored.ImplementationError, "f2 <- function(...) { f <- function() cat(...); assign(\"...\", NULL); f() }; f2(\"a\")");
         assertEval("f2 <- function(...) { f <- function() cat(...); assign(\"...\", \"asdf\"); f() }; f2(\"a\")");
 
         assertEval("{ g <- function(a,b,x) { a + b * x } ; f <- function(...) { g(x=4, ..., 10) }  ; f(b=1,a=2) }");
@@ -319,6 +324,8 @@ public class TestFunctions extends TestBase {
         assertEval("{ f <- function(a, barg, bextra, dummy) { a + barg } ; g <- function(...) { f(a=1, ..., xxx=2) } ; g(1) }");
         assertEval("{ f <- function(a, barg, bextra, dummy) { a + barg } ; g <- function(...) { f(a=1, xxx=2, ...) } ; g(1) }");
 
+        assertEval("{ `-.foo` <- function(...) 123; v <- 1; class(v) <- 'foo'; sapply(1,`-`,v); sapply(v,`-`,1); sapply(v,`-`,v) }");
+
         assertEval(Ignored.Unknown, "{ f <- function(...) { substitute(..1) } ;  f(x+y) }");
 
         assertEval(Ignored.Unknown, Output.ContainsError, "{ lapply(1:3, \"dummy\") }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTrace.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTrace.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a09217420b34d17cd213b0a9538913e0936e053
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestTrace.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016, 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.utils;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestTrace extends TestBase {
+    private static final String FASTR_TRACE = "(if (exists('.fastr.trace')) .fastr.trace else trace)";
+    private static final String PLAIN_TRACE = "trace";
+    private static final String[] TRACE_VARIANTS = new String[]{PLAIN_TRACE, FASTR_TRACE};
+
+    @Test
+    public void testSimple() {
+        assertEval(template("f <- function(x) {}; %0(f); f()", TRACE_VARIANTS));
+    }
+
+    @Test
+    public void testSimpleTrace() {
+        assertEval(template("f <- function(x) {}; %0(f, tracer=quote(print(x))); f(100)", TRACE_VARIANTS));
+    }
+
+    @Test
+    public void testMultiTrace() {
+        assertEval(template("f <- function(x) {}; %0(f, tracer=quote(print(x))); g <- function() for (i in 1:10) f(i); g()", TRACE_VARIANTS));
+    }
+
+    @Test
+    public void testCondTrace() {
+        assertEval(template("f <- function(x) {}; %0(f, tracer=quote(if (x == 3 || x == 7) print(x))); g <- function() for (i in 1:10) f(i); g()", TRACE_VARIANTS));
+    }
+
+}
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index db81248acc0e31a90b87084e44548dadc924e74a..39bdc1aaa7c197218691ebd726a6882c5860232d 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -171,7 +171,6 @@ com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java,purdue.copyright
-com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/AsS4.java,gnu_r_gentleman_ihaka.copyright