diff --git a/com.oracle.truffle.r.native/library/lib.mk b/com.oracle.truffle.r.native/library/lib.mk
index 085e5fa43163fc40ab8719348c9774235a3a1396..8de59dce0295f84495c1e14118d53a5a1c951620 100644
--- a/com.oracle.truffle.r.native/library/lib.mk
+++ b/com.oracle.truffle.r.native/library/lib.mk
@@ -89,6 +89,10 @@ $(C_OBJECTS): | $(OBJ)
 
 $(F_OBJECTS): | $(OBJ)
 
+$(GNUR_C_OBJECTS): | $(OBJ)
+
+$(GNUR_F_OBJECTS): | $(OBJ)
+
 $(OBJ):
 	mkdir -p $(OBJ)
 
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 68f75c9d98b62dea975c220f8c92a37e9de3d75a..6a4c3ffe80e4766e1b38365708f2c744b64e06b4 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
@@ -30,6 +30,8 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.profiles.*;
 import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.nodes.control.SequenceNode;
+import com.oracle.truffle.r.nodes.function.FunctionBodyNode;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
@@ -156,6 +158,20 @@ public abstract class Identical extends RBuiltinNode {
             }
             RSyntaxNode xNode = x.getRep().asRSyntaxNode();
             RSyntaxNode yNode = y.getRep().asRSyntaxNode();
+            // the following is (at least) needed by setGeneric function which expects a call node
+            // and function body node containing a single (same) call node to be identical
+            if (xNode instanceof FunctionBodyNode) {
+                xNode = ((FunctionBodyNode) xNode).getStatements();
+            }
+            if (yNode instanceof FunctionBodyNode) {
+                yNode = ((FunctionBodyNode) yNode).getStatements();
+            }
+            if (xNode instanceof SequenceNode && ((SequenceNode) xNode).getSequence().length == 1) {
+                xNode = ((SequenceNode) xNode).getSequence()[0].asRSyntaxNode();
+            }
+            if (yNode instanceof SequenceNode && ((SequenceNode) yNode).getSequence().length == 1) {
+                yNode = ((SequenceNode) yNode).getSequence()[0].asRSyntaxNode();
+            }
             return RRuntime.asLogical(xNode.getRequalsImpl(yNode));
         }
         return RRuntime.LOGICAL_FALSE;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
index 8548085db3135f95c5df12601582e4ec9c254c9f..89ac8adff35ecf324d816d1888350be296420676 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
@@ -41,9 +41,9 @@ import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 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.RFunction;
-import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 
 public class PrintFunctions {
@@ -102,7 +102,7 @@ public class PrintFunctions {
             // chacking for class attribute is a bit of a hack but GNU R has a hack in place here as
             // well to avoid recursively calling show via print in showDefault (we just can't use
             // the same hack at this point - for details see definition of showDefault in show.R)
-            return o instanceof RS4Object && ((RS4Object) o).getClassAttr(attrProfiles) != null;
+            return o instanceof RAttributable && ((RAttributable) o).isS4() && ((RAttributable) o).getClassAttr(attrProfiles) != null;
         }
 
     }
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 97c080069010e42467e5735b5fe86cb34d8bbd54..f02429010f90de132184f291f729592b38c7063d 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
@@ -110,11 +110,13 @@ public abstract class UnClass extends RBuiltinNode {
             objectProfile.enter();
             // TODO: should we make S4 objects shareable?
             RS4Object resultS4 = RDataFactory.createS4Object();
-            RAttributes newAttributes = resultS4.initAttributes();
-            for (RAttribute attr : arg.getAttributes()) {
-                newAttributes.put(attr.getName(), attr.getValue());
+            if (arg.getAttributes() != null) {
+                RAttributes newAttributes = resultS4.initAttributes();
+                for (RAttribute attr : arg.getAttributes()) {
+                    newAttributes.put(attr.getName(), attr.getValue());
+                }
+                newAttributes.remove(RRuntime.CLASS_ATTR_KEY);
             }
-            newAttributes.remove(RRuntime.CLASS_ATTR_KEY);
             return resultS4;
         }
         return arg;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index b3456492ebd032b929d2ab2c9d53b6814a701c45..73952a707cdb3b48aed7a571118c6e2928b3d996 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -43,6 +43,7 @@ import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -196,13 +197,17 @@ public abstract class UpdateAttr extends RInvisibleBuiltinNode {
      * All other, non-performance centric, {@link RAttributable} types.
      */
     @Fallback
-    protected Object updateAttr(Object object, Object name, Object value) {
+    protected Object updateAttr(Object obj, Object name, Object value) {
+        Object object = obj;
         controlVisibility();
         String sname = RRuntime.asString(name);
         if (sname == null) {
             errorProfile.enter();
             throw RError.error(this, RError.Message.MUST_BE_NONNULL_STRING, "name");
         }
+        if (object instanceof RShareable) {
+            object = ((RShareable) object).getNonShared();
+        }
         String internedName = intern(sname);
         if (object instanceof RAttributable) {
             RAttributable attributable = (RAttributable) object;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
index ef49e79dcf42cd57ec228d45c21a5f21ea24e40d..9acfb7ef6fa50094bbe2e272306833074ef06308 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
@@ -44,6 +44,7 @@ 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.RShareable;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -209,8 +210,12 @@ public abstract class UpdateAttributes extends RInvisibleBuiltinNode {
      */
     @Fallback
     @TruffleBoundary
-    public Object doOther(Object obj, Object operand) {
+    public Object doOther(Object o, Object operand) {
         controlVisibility();
+        Object obj = o;
+        if (obj instanceof RShareable) {
+            obj = ((RShareable) obj).getNonShared();
+        }
         if (obj instanceof RAttributable) {
             RAttributable attrObj = (RAttributable) obj;
             attrObj.removeAllAttributes();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
index 0d1ccdf2568737c62788c0bbdc1404348fcaee65..938971c931d5d81dea6f63876f148ff9d15f02ba 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
@@ -17,7 +17,10 @@ import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.attributes.PutAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.PutAttributeNodeGen;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
+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.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.*;
 
 @NodeChildren({@NodeChild(value = "object", type = RNode.class), @NodeChild(value = "name", type = RNode.class), @NodeChild(value = "value", type = RNode.class)})
@@ -34,17 +37,39 @@ public abstract class UpdateSlotNode extends RNode {
     }
 
     @SuppressWarnings("unused")
-    @Specialization(guards = "name == cachedName")
+    @Specialization(guards = {"!isData(name)", "name == cachedName"})
     protected Object updateSlotS4Cached(RAttributable object, String name, Object value, @Cached("name") String cachedName, @Cached("createAttrUpdate(cachedName)") PutAttributeNode attributeUpdate) {
         attributeUpdate.execute(object.initAttributes(), value);
         return object;
     }
 
-    @Specialization(contains = "updateSlotS4Cached")
+    @Specialization(contains = "updateSlotS4Cached", guards = "!isData(name)")
     protected Object updateSlotS4(RAttributable object, String name, Object value) {
         assert name == name.intern();
         object.setAttr(name, value);
         return object;
     }
 
+    protected RFunction setDataPartFunction(REnvironment methodsNamespace) {
+        Object f = methodsNamespace.findFunction("setDataPart");
+        return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f);
+    }
+
+    private Object setDataPart(RAttributable object, Object value) {
+        // TODO: any way to cache it or use a mechanism similar to overrides?
+        REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
+        RFunction dataPart = setDataPartFunction(methodsNamespace);
+        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), object, value, RRuntime.LOGICAL_TRUE);
+    }
+
+    @Specialization(guards = "isData(name)")
+    protected Object updateSlotS4Data(RAttributable object, @SuppressWarnings("unused") String name, Object value) {
+        return setDataPart(object, value);
+    }
+
+    protected boolean isData(String name) {
+        assert name == name.intern();
+        return name == RRuntime.DOT_DATA;
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index deeae0c09029ec750993a289b9287b4ad2ccd391..2da71801a5d9e4c807ef023b47c939b0ffd2f03b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -131,7 +131,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     public RRootNode duplicateWithNewFrameDescriptor() {
         FrameDescriptor frameDesc = new FrameDescriptor();
         FrameSlotChangeMonitor.initializeFunctionFrameDescriptor(description != null && !description.isEmpty() ? description : "<function>", frameDesc);
-        return new FunctionDefinitionNode(getSourceSection(), frameDesc, (BodyNode) body.deepCopy(), getFormalArguments(), description, substituteFrame, argPostProcess == null ? null
+        return new FunctionDefinitionNode(getSourceSection(), frameDesc, (BodyNode) body.unwrap().deepCopy(), getFormalArguments(), description, substituteFrame, argPostProcess == null ? null
                         : (PostProcessArgumentsNode) argPostProcess.deepCopy());
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/DuplicateNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/DuplicateNode.java
index 0c236232eb884c7cfba5ce4b9bfd2dadbc44f4ba..3b883e9072c7d1c39abae06a0f5e37b0bbcd4d84 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/DuplicateNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/DuplicateNode.java
@@ -51,18 +51,18 @@ public abstract class DuplicateNode extends RBaseNode {
     @Specialization
     protected Object duplicate(RS4Object object) {
         RS4Object newObject = RDataFactory.createS4Object();
-        RAttributes newAttributes = newObject.initAttributes();
-        for (RAttribute attr : object.getAttributes()) {
-            newAttributes.put(attr.getName(), attr.getValue());
+        if (object.getAttributes() != null) {
+            RAttributes newAttributes = newObject.initAttributes();
+            for (RAttribute attr : object.getAttributes()) {
+                newAttributes.put(attr.getName(), attr.getValue());
+            }
         }
         return newObject;
     }
 
     @Specialization
     protected Object duplicate(RFunction f) {
-        RFunction newObject = RDataFactory.createFunction(f.getName(), f.getTarget(), f.getRBuiltin(), f.getEnclosingFrame(), f.getFastPath(), f.containsDispatch());
-        newObject.initAttributes(f.getAttributes());
-        return newObject;
+        return f.copy();
     }
 
     // TODO: support more types when required
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 1c67585506a6fbb8b8ee55b0b0a7c3bdce67e28e..60b1220653c7b6136e0c1fa0b08f80567e610033 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
@@ -1522,6 +1522,10 @@ public class RSerialize {
                         break;
                     }
 
+                    case S4SXP: {
+                        break;
+                    }
+
                     /*
                      * FastR scalar, (length 1) "vectors"
                      */
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 1b200f55b9b0c25a1eeaa6232b36473f5a6a54da..d76cfa027347f061a5046857ad5263e420547cde 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
@@ -105,7 +105,11 @@ public interface RAttributable extends RTypedValue {
     }
 
     default RAttributable setClassAttr(RStringVector classAttr, @SuppressWarnings("unused") boolean convertToInt) {
-        setAttr(RRuntime.CLASS_ATTR_KEY, classAttr);
+        if (classAttr == null && getAttributes() != null) {
+            getAttributes().remove(RRuntime.CLASS_ATTR_KEY);
+        } else {
+            setAttr(RRuntime.CLASS_ATTR_KEY, classAttr);
+        }
         return this;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
index b0e0c220351de0c53e8da4d81b5744d7d801b562..04487a6bdbc27bd7429fa309873d769b75c4aa5b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java
@@ -85,7 +85,7 @@ public final class RDataFrame implements RShareable, RAbstractContainer {
     }
 
     @Override
-    public RVector makeShared() {
+    public RSharingAttributeStorage makeShared() {
         return vector.makeShared();
     }
 
@@ -109,6 +109,12 @@ public final class RDataFrame implements RShareable, RAbstractContainer {
         vector.makeSharedPermanent();
     }
 
+    @Override
+    public RShareable getNonShared() {
+        RVector newVector = (RVector) vector.getNonShared();
+        return newVector == vector ? this : RDataFactory.createDataFrame(newVector);
+    }
+
     @Override
     public RDataFrame copy() {
         return RDataFactory.createDataFrame(vector.copy());
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 8c8cecd7b3dbb661c4042b69e4c3cb0c5c821722..6075944def458fb234b54f4ad1e75f2f53a309f4 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
@@ -180,7 +180,7 @@ public class RExpression implements RShareable, RAbstractContainer {
     }
 
     @Override
-    public RVector makeShared() {
+    public RSharingAttributeStorage makeShared() {
         return data.makeShared();
     }
 
@@ -204,6 +204,12 @@ public class RExpression implements RShareable, RAbstractContainer {
         data.makeSharedPermanent();
     }
 
+    @Override
+    public RShareable getNonShared() {
+        RList newData = (RList) data.getNonShared();
+        return newData == data ? this : RDataFactory.createExpression(newData);
+    }
+
     @Override
     public RExpression copy() {
         return RDataFactory.createExpression((RList) data.copy());
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 1a6f0290daf87d1a2a57122439c1c7f65d4bcd4f..757bbe08758a0ee7dd08ca82b945402391e60974 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
@@ -83,7 +83,7 @@ public final class RFactor implements RShareable, RAbstractContainer {
     }
 
     @Override
-    public RVector makeShared() {
+    public RSharingAttributeStorage makeShared() {
         return vector.makeShared();
     }
 
@@ -107,6 +107,12 @@ public final class RFactor implements RShareable, RAbstractContainer {
         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);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
index f3632b1d57f423a2f878bbb363ecf5f7cdc01c29..17108cea26d122649cf52ef4ead713f758c78c74 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
@@ -31,6 +31,7 @@ import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.VirtualEvalFrame;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 
 /**
  * An instance of {@link RFunction} represents a function defined in R. The properties of a function
@@ -44,7 +45,7 @@ import com.oracle.truffle.r.runtime.context.RContext;
  * {@link #enclosingFrame}.
  * </ul>
  */
-public final class RFunction extends RAttributeStorage implements RTypedValue, TruffleObject {
+public final class RFunction extends RSharingAttributeStorage implements RTypedValue, TruffleObject {
 
     public static final String NO_NAME = new String("");
 
@@ -126,4 +127,19 @@ public final class RFunction extends RAttributeStorage implements RTypedValue, T
     public void setFastPath(FastPathFactory fastPath) {
         this.fastPath = fastPath;
     }
+
+    @Override
+    public RShareable copy() {
+        RFunction newFunction = RDataFactory.createFunction(getName(), getTarget(), getRBuiltin(), getEnclosingFrame(), getFastPath(), containsDispatch());
+        if (getAttributes() != null) {
+            RAttributes newAttributes = newFunction.initAttributes();
+            for (RAttribute attr : getAttributes()) {
+                newAttributes.put(attr.getName(), attr.getValue());
+            }
+            newFunction.initAttributes(newAttributes);
+        }
+        return newFunction;
+
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
index 3110983e73cc70e6b85a5ee2c03805b65555a13a..2cd03ee404c6aac63257ca362cd8da4c571eebf2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
@@ -36,11 +36,6 @@ import com.oracle.truffle.r.runtime.nodes.*;
  * instances. R allows a language element to be treated as a list, hence the support for
  * {@link RAbstractContainer}, which is implemented via AST walk operations.
  *
- * The representation is inherited from {@link RLanguageRep}. This is a Truffle AST ({@code RNode}),
- * although that type is not statically used here due to project circularities. A related
- * consequence is the the implementation of the {@link RAbstractContainer} methods are delegated to
- * a helper class from a project that can access {@code RNode}.
- *
  * {@link RLanguage} instances are almost completely immutable, <i>except</i> for the ability for
  * {@code}names} updates which manifests itself as actually transforming the AST. S we do have to
  * implement the {@link RShareable} interface.
@@ -48,11 +43,9 @@ import com.oracle.truffle.r.runtime.nodes.*;
  *
  */
 @ValueType
-public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttributable, RShareable {
-
-    private RAttributes attributes;
+public class RLanguage extends RSharingAttributeStorage implements RAbstractContainer, RAttributable, RShareable {
 
-    private int gpbits;
+    private RBaseNode rep;
 
     /**
      * Lazily computed value.
@@ -60,31 +53,24 @@ public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttr
     private int length = -1;
 
     RLanguage(RNode rep) {
-        super(rep);
+        this.rep = rep;
     }
 
     RLanguage(RNode rep, int length) {
-        super(rep);
+        this.rep = rep;
         this.length = length;
     }
 
-    public RType getRType() {
-        return RType.Language;
+    public RBaseNode getRep() {
+        return rep;
     }
 
-    public RAttributes getAttributes() {
-        return attributes;
+    public void setRep(RBaseNode rep) {
+        this.rep = rep;
     }
 
-    public RAttributes initAttributes() {
-        if (attributes == null) {
-            attributes = RAttributes.create();
-        }
-        return attributes;
-    }
-
-    public final void initAttributes(RAttributes newAttributes) {
-        attributes = newAttributes;
+    public RType getRType() {
+        return RType.Language;
     }
 
     public boolean isComplete() {
@@ -124,15 +110,7 @@ public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttr
     }
 
     public RLanguage materializeNonShared() {
-        if (this.isShared()) {
-            RLanguage res = this.copy();
-            res.markNonTemporary();
-            return res;
-        }
-        if (this.isTemporary()) {
-            this.markNonTemporary();
-        }
-        return this;
+        return (RLanguage) getNonShared();
     }
 
     public RShareable materializeToShareable() {
@@ -183,10 +161,7 @@ public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttr
         setAttr(RRuntime.ROWNAMES_ATTR_KEY, rowNames);
     }
 
-    public RStringVector getClassHierarchy() {
-        return RDataFactory.createStringVector(RRuntime.CLASS_LANGUAGE);
-    }
-
+    @Override
     public RStringVector getImplicitClass() {
         return RDataFactory.createStringVector(RRuntime.CLASS_LANGUAGE);
     }
@@ -206,78 +181,9 @@ public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttr
         return l;
     }
 
-    /*
-     * RShareable support. Code is cloned directly from RVector.
-     */
-    private boolean shared;
-    private boolean temporary = true;
-    private int refCount;
-
-    public void markNonTemporary() {
-        assert !FastROptions.NewStateTransition.getBooleanValue();
-        temporary = false;
-    }
-
-    public boolean isTemporary() {
-        if (FastROptions.NewStateTransition.getBooleanValue()) {
-            return refCount == 0;
-        } else {
-            return temporary;
-        }
-    }
-
-    public boolean isShared() {
-        if (FastROptions.NewStateTransition.getBooleanValue()) {
-            return refCount > 1;
-        } else {
-            return shared;
-        }
-    }
-
-    public RShareable makeShared() {
-        assert !FastROptions.NewStateTransition.getBooleanValue();
-        if (temporary) {
-            temporary = false;
-        }
-        shared = true;
-        return this;
-    }
-
-    public void incRefCount() {
-        refCount++;
-    }
-
-    public void decRefCount() {
-        assert refCount > 0;
-        refCount--;
-    }
-
-    @Override
-    public boolean isSharedPermanent() {
-        return refCount == SHARED_PERMANENT_VAL;
-    }
-
-    @Override
-    public void makeSharedPermanent() {
-        if (FastROptions.NewStateTransition.getBooleanValue()) {
-            refCount = SHARED_PERMANENT_VAL;
-        } else {
-            // old scheme never reverts states
-            makeShared();
-        }
-    }
-
     @Override
     public String toString() {
         return String.format("RLanguage(rep=%s)", getRep());
     }
 
-    public int getGPBits() {
-        return gpbits;
-    }
-
-    public void setGPBits(int value) {
-        gpbits = value;
-    }
-
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguageRep.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguageRep.java
deleted file mode 100644
index ad923c780644e1d4b0259eef519903c2bfb65bab..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguageRep.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.runtime.data;
-
-import com.oracle.truffle.r.runtime.nodes.*;
-
-/**
- * Denotes an (unevaluated) element of the R language.
- *
- * This type is not part of the {@code TypeSystem} but it used as a superclass by {@link RLanguage}
- * and {@link RPromise}.
- */
-public abstract class RLanguageRep {
-
-    private RBaseNode rep;
-
-    public RLanguageRep(RBaseNode rep) {
-        this.rep = rep;
-    }
-
-    public RBaseNode getRep() {
-        return rep;
-    }
-
-    public void setRep(RBaseNode rep) {
-        this.rep = rep;
-    }
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
index b440411fb8ce778c888e4bb47d05a423d2691920..8dd712e267ce30feb03e298fd255d663d7bc2e92 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java
@@ -38,7 +38,7 @@ import com.oracle.truffle.r.runtime.nodes.*;
  * which is used for manual method dispatch.
  */
 @ValueType
-public class RPromise extends RLanguageRep implements RTypedValue {
+public class RPromise implements RTypedValue {
 
     /**
      * Different to GNU R, FastR has no additional binding information (a "origin" where the binding
@@ -79,6 +79,8 @@ public class RPromise extends RLanguageRep implements RTypedValue {
 
     public static final String CLOSURE_WRAPPER_NAME = new String("<promise>");
 
+    private RBaseNode rep;
+
     /**
      * @see PromiseType
      */
@@ -112,7 +114,7 @@ public class RPromise extends RLanguageRep implements RTypedValue {
      * This creates a new tuple (expr, env, closure, value=null), which may later be evaluated.
      */
     RPromise(PromiseType type, OptType optType, MaterializedFrame execFrame, Closure closure) {
-        super(closure.getExpr());
+        this.rep = closure.getExpr();
         assert type != PromiseType.ARG_DEFAULT || execFrame != null;
         this.type = type;
         this.optType = optType;
@@ -124,7 +126,7 @@ public class RPromise extends RLanguageRep implements RTypedValue {
      * This creates a new tuple (expr, null, null, value), which is already evaluated.
      */
     RPromise(PromiseType type, OptType optType, RBaseNode expr, Object value) {
-        super(expr);
+        this.rep = expr;
         assert value != null;
         this.type = type;
         this.optType = optType;
@@ -139,7 +141,7 @@ public class RPromise extends RLanguageRep implements RTypedValue {
      * called via {@link VarargPromise#VarargPromise(PromiseType, RPromise, Closure)} only!
      */
     private RPromise(PromiseType type, OptType optType, RBaseNode expr) {
-        super(expr);
+        this.rep = expr;
         this.type = type;
         this.optType = optType;
         // Not needed as already evaluated:
@@ -147,6 +149,14 @@ public class RPromise extends RLanguageRep implements RTypedValue {
         this.closure = null;
     }
 
+    public RBaseNode getRep() {
+        return rep;
+    }
+
+    public void setRep(RBaseNode rep) {
+        this.rep = rep;
+    }
+
     public RType getRType() {
         return RType.Promise;
     }
@@ -192,15 +202,6 @@ public class RPromise extends RLanguageRep implements RTypedValue {
         return frame == execFrame;
     }
 
-    /**
-     * @return The representation of expression (a RNode). May contain <code>null</code> if no expr
-     *         is provided!
-     */
-    @Override
-    public final RBaseNode getRep() {
-        return super.getRep();
-    }
-
     /**
      * @return {@link #closure}
      */
@@ -485,4 +486,9 @@ public class RPromise extends RLanguageRep implements RTypedValue {
         throw RInternalError.shouldNotReachHere();
     }
 
+    @Override
+    public boolean isS4() {
+        return false;
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalar.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalar.java
index c33f21afb8a19358ac403a80edfe6f58426938b8..869c48e8c691f3911feedb2d02ebea78e466d635 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalar.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalar.java
@@ -38,4 +38,9 @@ public abstract class RScalar implements RTypedValue {
         throw RInternalError.shouldNotReachHere();
     }
 
+    @Override
+    public boolean isS4() {
+        return false;
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
index e112e6b5047510aadb810d236d4708edf6ae17a7..3c407bdb435d835b9a481f567bc9ab00bcc9c7bf 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
@@ -199,4 +199,9 @@ public abstract class RSequence implements RAbstractVector {
         throw RInternalError.shouldNotReachHere();
     }
 
+    @Override
+    public boolean isS4() {
+        return false;
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RShareable.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RShareable.java
index b929568d6a3bdc488a8f9e35c28c09d678786c75..9d92ad3f944fc4520ac7e127240921c883515aa5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RShareable.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RShareable.java
@@ -52,4 +52,6 @@ public interface RShareable {
 
     void makeSharedPermanent();
 
+    RShareable getNonShared();
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSharingAttributeStorage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSharingAttributeStorage.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a29ac41f8b5709a370775b4e129ad2a8daf94ed
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSharingAttributeStorage.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.data;
+
+import com.oracle.truffle.r.runtime.*;
+
+/**
+ * An adaptor class for the several R types that are both attributable and shareable.
+ */
+public abstract class RSharingAttributeStorage extends RAttributeStorage implements RShareable {
+
+    private int refCount;
+
+    private static final int TEMPORARY = 0x1;
+    private static final int SHARED = 0x2;
+
+    protected RSharingAttributeStorage() {
+        if (!FastROptions.NewStateTransition.getBooleanValue()) {
+            refCount = TEMPORARY;
+        }
+    }
+
+    @Override
+    public final void markNonTemporary() {
+        assert !FastROptions.NewStateTransition.getBooleanValue();
+        refCount &= ~TEMPORARY;
+    }
+
+    @Override
+    public final boolean isTemporary() {
+        if (FastROptions.NewStateTransition.getBooleanValue()) {
+            return refCount == 0;
+        } else {
+            return (refCount & TEMPORARY) != 0;
+        }
+    }
+
+    @Override
+    public final boolean isShared() {
+        if (FastROptions.NewStateTransition.getBooleanValue()) {
+            return refCount > 1;
+        } else {
+            return (refCount & SHARED) != 0;
+        }
+    }
+
+    @Override
+    public final RSharingAttributeStorage makeShared() {
+        assert !FastROptions.NewStateTransition.getBooleanValue();
+        refCount = SHARED;
+        return this;
+    }
+
+    @Override
+    public final void incRefCount() {
+        refCount++;
+    }
+
+    @Override
+    public final void decRefCount() {
+        assert refCount > 0;
+        refCount--;
+    }
+
+    @Override
+    public boolean isSharedPermanent() {
+        return refCount == SHARED_PERMANENT_VAL;
+    }
+
+    @Override
+    public void makeSharedPermanent() {
+        if (FastROptions.NewStateTransition.getBooleanValue()) {
+            refCount = SHARED_PERMANENT_VAL;
+        } else {
+            // old scheme never reverts states
+            makeShared();
+        }
+    }
+
+    public RShareable getNonShared() {
+        if (this.isShared()) {
+            RShareable res = this.copy();
+            if (FastROptions.NewStateTransition.getBooleanValue()) {
+                assert res.isTemporary();
+                res.incRefCount();
+            } else {
+                res.markNonTemporary();
+            }
+            return res;
+        }
+        if (this.isTemporary()) {
+            // this is needed for primitive value coercion - they need to be marked as
+            // non-temp, otherwise the following code will not work:
+            // x<-1; attributes(x) <- list(my = 1); y<-x; attributes(y)<-list(his = 2); x
+            if (FastROptions.NewStateTransition.getBooleanValue()) {
+                this.incRefCount();
+            } else {
+                this.markNonTemporary();
+            }
+        }
+        return this;
+    }
+
+}
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 1f3cfb2aa5faab424d6b48e913ba0c53c6b393c2..74e5f21fa6e931fbc04a19a9d6dbf60b2e4bf27f 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
@@ -49,7 +49,7 @@ import com.oracle.truffle.r.runtime.nodes.*;
  * - non-shared => shared
  * </pre>
  */
-public abstract class RVector extends RAttributeStorage implements RShareable, RAbstractVector, RFFIAccess {
+public abstract class RVector extends RSharingAttributeStorage implements RShareable, RAbstractVector, RFFIAccess {
 
     private static final RStringVector implicitClassHeaderArray = RDataFactory.createStringVector(new String[]{RType.Array.getName()}, true);
     private static final RStringVector implicitClassHeaderMatrix = RDataFactory.createStringVector(new String[]{RType.Matrix.getName()}, true);
@@ -60,19 +60,12 @@ public abstract class RVector extends RAttributeStorage implements RShareable, R
     private RList dimNames;
     // cache rownames for data frames as they are accessed at every data frame access
     private Object rowNames;
-    private int refCount;
-
-    private static final int TEMPORARY = 0x1;
-    private static final int SHARED = 0x2;
 
     protected RVector(boolean complete, int length, int[] dimensions, RStringVector names) {
         this.complete = complete;
         this.dimensions = dimensions;
         this.names = names;
         this.rowNames = RNull.instance;
-        if (!FastROptions.NewStateTransition.getBooleanValue()) {
-            refCount = TEMPORARY;
-        }
         if (names != null) {
             // since this constructor is for internal use only, the assertion shouldn't fail
             assert names.getLength() == length : "size mismatch: " + names.getLength() + " vs. " + length;
@@ -423,63 +416,6 @@ public abstract class RVector extends RAttributeStorage implements RShareable, R
         return complete;
     }
 
-    @Override
-    public final void markNonTemporary() {
-        assert !FastROptions.NewStateTransition.getBooleanValue();
-        refCount &= ~TEMPORARY;
-    }
-
-    @Override
-    public final boolean isTemporary() {
-        if (FastROptions.NewStateTransition.getBooleanValue()) {
-            return refCount == 0;
-        } else {
-            return (refCount & TEMPORARY) != 0;
-        }
-    }
-
-    @Override
-    public final boolean isShared() {
-        if (FastROptions.NewStateTransition.getBooleanValue()) {
-            return refCount > 1;
-        } else {
-            return (refCount & SHARED) != 0;
-        }
-    }
-
-    @Override
-    public final RVector makeShared() {
-        assert !FastROptions.NewStateTransition.getBooleanValue();
-        refCount = SHARED;
-        return this;
-    }
-
-    @Override
-    public final void incRefCount() {
-        refCount++;
-    }
-
-    @Override
-    public final void decRefCount() {
-        assert refCount > 0;
-        refCount--;
-    }
-
-    @Override
-    public boolean isSharedPermanent() {
-        return refCount == SHARED_PERMANENT_VAL;
-    }
-
-    @Override
-    public void makeSharedPermanent() {
-        if (FastROptions.NewStateTransition.getBooleanValue()) {
-            refCount = SHARED_PERMANENT_VAL;
-        } else {
-            // old scheme never reverts states
-            makeShared();
-        }
-    }
-
     public final boolean hasDimensions() {
         return dimensions != null;
     }
@@ -855,28 +791,8 @@ public abstract class RVector extends RAttributeStorage implements RShareable, R
     }
 
     @Override
-    public final RVector materializeNonShared() {
-        if (this.isShared()) {
-            RVector res = this.copy();
-            if (FastROptions.NewStateTransition.getBooleanValue()) {
-                assert res.isTemporary();
-                res.incRefCount();
-            } else {
-                res.markNonTemporary();
-            }
-            return res;
-        }
-        if (this.isTemporary()) {
-            // this is needed for primitive values coerced to vector - they need to be marked as
-            // non-temp, otherwise the following code will not work:
-            // x<-1; attributes(x) <- list(my = 1); y<-x; attributes(y)<-list(his = 2); x
-            if (FastROptions.NewStateTransition.getBooleanValue()) {
-                this.incRefCount();
-            } else {
-                this.markNonTemporary();
-            }
-        }
-        return this;
+    public RVector materializeNonShared() {
+        return (RVector) getNonShared();
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
index 6fef32a799a3c20b1573ee874847b2bc00220679..a0baa2b12d372960943cdde06c6e5e16066d5616 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
@@ -184,4 +184,9 @@ public abstract class RToVectorClosure implements RAbstractVector {
         vector.setGPBits(value);
     }
 
+    @Override
+    public boolean isS4() {
+        return false;
+    }
+
 }
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 5fac432ac6303c5bc066df6bb0154826237ea84e..2d2654f8efdca8112f7126a7a198f58b4be00c7a 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
@@ -46,7 +46,7 @@ public enum SEXPTYPE {
     EXTPTRSXP(22, RExternalPtr.class), /* external pointer */
     WEAKREFSXP(23), /* weak reference */
     RAWSXP(24, new Class<?>[]{RRawVector.class, RRaw.class}), /* raw bytes */
-    S4SXP(25), /* S4 non-vector */
+    S4SXP(25, RS4Object.class), /* S4 non-vector */
 
     NEWSXP(30), /* fresh node created in new page */
     FREESXP(31), /* node released by GC */
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 f6880d4a82f9d94c583dd9311592551b1e298d19..524dfb633e07580e16dcf8788e0712d139485df9 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
@@ -39,6 +39,26 @@ Error: unexpected '}' in "{ asS4(NULL); isS4(NULL }"
 #{ x<-42; y<-asS4(x); isS4(y) }
 [1] TRUE
 
+##com.oracle.truffle.r.test.S4.TestS4.testMethods
+#{ gen<-function(object) 0; setGeneric("gen"); gen }
+standardGeneric for "gen" defined from package ".GlobalEnv"
+
+function (object)
+standardGeneric("gen")
+<environment: 0x100835ce8>
+Methods may be defined for arguments: object
+Use  showMethods("gen")  for currently available ones.
+
+##com.oracle.truffle.r.test.S4.TestS4.testMethods
+#{ setGeneric("gen", function(object) standardGeneric("gen")); gen }
+standardGeneric for "gen" defined from package ".GlobalEnv"
+
+function (object)
+standardGeneric("gen")
+<environment: 0x101efac38>
+Methods may be defined for arguments: object
+Use  showMethods("gen")  for currently available ones.
+
 ##com.oracle.truffle.r.test.S4.TestS4.testSlotAccess
 #{ `@`(getClass("ClassUnionRepresentation"), "virtual") }
 [1] FALSE
@@ -11774,6 +11794,10 @@ attr(,"package")
 #{ x <- new.env(); class(x); class(x)<-c("abc", "xyz"); class(x); class(x)<-NULL; class(x) }
 [1] "environment"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_classassign.testUpdateClass
+#{ x<-function() 42; class(x)<-"foo"; class(x)<-NULL; x }
+function() 42
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_classassign.testUpdateClass
 #{ x=1;class(x)<-"character"; x}
 [1] "1"
@@ -56577,6 +56601,12 @@ $dimnames[[2]]
 
 
 
+##com.oracle.truffle.r.test.library.base.TestSimpleAttributes.testDefinition
+#{ x<-function() 42; attr(x, "foo")<-"foo"; y<-x; attr(y, "foo")<-NULL; x }
+function() 42
+attr(,"foo")
+[1] "foo"
+
 ##com.oracle.truffle.r.test.library.base.TestSimpleAttributes.testOtherPropagation
 #{ x <- 1:2;  attr(x, "hi") <- 2 ;  x == x }
 [1] TRUE TRUE
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java
index 841483337f558eeba372a8617c6242cd8830c321..05b3c4b9de774b1b77f0995b610a6f7783549203 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java
@@ -82,4 +82,11 @@ public class TestS4 extends TestBase {
         // output slightly different from GNU R even though we use R's "show" method to print it
         assertEval(Ignored.OutputFormatting, "{ setClass(\"foo\", representation(j=\"numeric\")); getClass(\"foo\") }");
     }
+
+    @Test
+    public void testMethods() {
+        // output slightly different from GNU R even though we use R's "show" method to print it
+        assertEval(Ignored.OutputFormatting, "{ setGeneric(\"gen\", function(object) standardGeneric(\"gen\")); gen }");
+        assertEval(Ignored.OutputFormatting, "{ gen<-function(object) 0; setGeneric(\"gen\"); gen }");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_classassign.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_classassign.java
index e9eb18ac71eb5eeb10f2a049c41862fad4cdfa71..7f4704b531f74bece273094a3e0b6be5a0640421 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_classassign.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_classassign.java
@@ -184,5 +184,7 @@ public class TestBuiltin_classassign extends TestBase {
 
         assertEval(Output.ContainsError, "{x<-c(1,2,3,4); class(x)<-\"array\"; class(x)<-\"matrix\";}");
         assertEval(Output.ContainsError, "{x<-1;attr(x,\"class\")<-c(1,2,3);}");
+
+        assertEval("{ x<-function() 42; class(x)<-\"foo\"; class(x)<-NULL; x }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleAttributes.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleAttributes.java
index 1d7ded250087787d1b3e9d65032273a81940cbec..3f8f17e223d37780285308bf27ec19ad62d8e6ff 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleAttributes.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleAttributes.java
@@ -42,6 +42,8 @@ public class TestSimpleAttributes extends TestBase {
         assertEval("{ x<-1:4; attributes(x)<-list(dim=c(2,2), dimnames=list(c(1,2), c(3,4))); attributes(x) }");
 
         assertEval("{ attributes(NULL) }");
+
+        assertEval("{ x<-function() 42; attr(x, \"foo\")<-\"foo\"; y<-x; attr(y, \"foo\")<-NULL; x }");
     }
 
     @Test