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/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
index 3e7d905c8275e80c958c998808ac306e8363502e..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,50 +27,21 @@ 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.RBuiltinNode;
-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.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.nodes.unary.PrecedenceNode;
-import com.oracle.truffle.r.nodes.unary.PrecedenceNodeGen;
-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;
@@ -387,35 +358,6 @@ public abstract class Bind extends RBaseNode {
 
     @Child private InheritsCheckNode inheritsCheck = new InheritsCheckNode(RRuntime.CLASS_DATA_FRAME);
 
-    public static final 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;
-        }
-
-        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;
-                    }
-                }
-            }
-            return false;
-        }
-    }
-
     protected boolean isDataFrame(Object[] args) {
         for (int i = 0; i < args.length; i++) {
             if (inheritsCheck.execute(args[i])) {
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/EnvFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
index 12ba9db3e2243e0fba60f880c019c10299616e8b..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
@@ -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/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/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/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/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/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/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/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 e0ccd70c7c8191564845a1475ac01db66182a6a7..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;
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/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/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/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/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/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.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 122d0bf014cc61f7da2d7319a96d271020482015..be36e61f8d8844183a60d73bd9911547f991b278 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
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) }");