From 84c6cdd4d3470d510bf9f9aa5ed6663aab699f90 Mon Sep 17 00:00:00 2001
From: "Prahlad Joshi joshi18@purdue.edu" <Prahlad Joshi joshi18@purdue.edu>
Date: Sun, 23 Feb 2014 21:58:32 -0500
Subject: [PATCH] Adding support for S3 class attribute

---
 .../r/nodes/builtin/base/BasePackage.java     |   2 +
 .../r/nodes/builtin/base/GetClass.java        |  29 +++
 .../r/nodes/builtin/base/IsObject.java        |   3 +-
 .../r/nodes/builtin/base/UpdateAttr.java      |   3 +
 .../r/nodes/builtin/base/UpdateClass.java     | 217 ++++++++++++++++++
 .../r/nodes/builtin/base/UseMethod.java       | 185 +++++++++++----
 .../com/oracle/truffle/r/runtime/RError.java  |  18 +-
 .../oracle/truffle/r/runtime/RRuntime.java    |   6 +
 .../r/runtime/data/RComplexVector.java        |   6 +
 .../r/runtime/data/RDoubleSequence.java       |  12 +
 .../truffle/r/runtime/data/RDoubleVector.java |   6 +
 .../truffle/r/runtime/data/RIntSequence.java  |  11 +
 .../truffle/r/runtime/data/RIntVector.java    |   7 +
 .../oracle/truffle/r/runtime/data/RList.java  |   7 +
 .../r/runtime/data/RLogicalVector.java        |   7 +
 .../truffle/r/runtime/data/RRawVector.java    |   7 +
 .../truffle/r/runtime/data/RSequence.java     |   8 +
 .../truffle/r/runtime/data/RStringVector.java |   7 +
 .../truffle/r/runtime/data/RVector.java       |  20 ++
 .../data/closures/RToVectorClosure.java       |  11 +
 .../r/runtime/data/model/RAbstractVector.java |   6 +
 mx.fastr/copyrights/overrides                 |   2 +
 22 files changed, 527 insertions(+), 53 deletions(-)
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java

diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 987847822b..5c9bfbb185 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -88,6 +88,7 @@ public class BasePackage extends RPackage {
         load(Exists.class);
         load(Floor.class);
         load(Get.class);
+        load(GetClass.class);
         load(Gregexpr.class);
         load(GSub.class);
         load(Ifelse.class);
@@ -166,6 +167,7 @@ public class BasePackage extends RPackage {
         load(Unlist.class);
         load(UpdateAttr.class);
         load(UpdateAttributes.class);
+        load(UpdateClass.class);
         load(UpdateDiag.class);
         load(UpdateDim.class);
         load(UpdateDimNames.class);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java
new file mode 100644
index 0000000000..edb1088261
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java
@@ -0,0 +1,29 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2014, Purdue University
+ * Copyright (c) 2014, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+
+package com.oracle.truffle.r.nodes.builtin.base;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@RBuiltin(value = "class")
+public abstract class GetClass extends RBuiltinNode {
+    @Specialization
+    public Object getClass(VirtualFrame frame, RAbstractVector arg) {
+        if (arg.isObject()) {
+            return arg.getAttributes().get(RRuntime.CLASS_ATTR_KEY);
+        }
+        return arg.getClassHierarchy().get(0);
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsObject.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsObject.java
index 9726598a29..33e2ac4c34 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsObject.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/IsObject.java
@@ -40,9 +40,8 @@ public abstract class IsObject extends RBuiltinNode {
     }
 
     @Specialization
-    @SuppressWarnings("unused")
     public byte isObject(RAbstractVector arg) {
         Map<String, Object> attributes = arg.getAttributes();
-        return (attributes != null && attributes.get(RRuntime.CLASS_ATTR_KEY) != null) ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
+        return arg.isObject() ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index ce0694b512..f55139c0bb 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -120,6 +120,9 @@ public abstract class UpdateAttr extends RBuiltinNode {
         } else if (name.equals(RRuntime.DIMNAMES_ATTR_KEY)) {
             return updateDimNames(frame, resultVector, value);
         } else {
+            if (name.equals(RRuntime.CLASS_ATTR_KEY) && !(value instanceof RString && value instanceof RStringVector)) {
+                RError.getInvalidClassAttr(getEncapsulatingSourceSection());
+            }
             if (resultVector.getAttributes() == null) {
                 resultVector.setAttributes(new LinkedHashMap<String, Object>());
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
new file mode 100644
index 0000000000..9d51e8beef
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
@@ -0,0 +1,217 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2014, Purdue University
+ * Copyright (c) 2014, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.builtin.base;
+
+import java.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+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.*;
+
+@RBuiltin(value = "class<-")
+public abstract class UpdateClass extends RBuiltinNode {
+
+    private RVector resultVector;
+    @Child private CastStringNode castStringNode;
+    @Child private CastComplexNode castComplexNode;
+    @Child private CastDoubleNode castDoubleNode;
+    @Child private CastIntegerNode castIntegerNode;
+    @Child private CastLogicalNode castLogicalNode;
+    @Child private CastRawNode castRawNode;
+
+    private void initCastString() {
+        if (castStringNode == null) {
+            CompilerDirectives.transferToInterpreter();
+            castStringNode = adoptChild(CastStringNodeFactory.create(null, false, false, false));
+        }
+    }
+
+    private void initCastComplex() {
+        if (castComplexNode == null) {
+            CompilerDirectives.transferToInterpreter();
+            castComplexNode = adoptChild(CastComplexNodeFactory.create(null, false, false));
+        }
+    }
+
+    private void initCastDouble() {
+        if (castDoubleNode == null) {
+            CompilerDirectives.transferToInterpreter();
+            castDoubleNode = adoptChild(CastDoubleNodeFactory.create(null, false, false));
+        }
+    }
+
+    private void initCastInteger() {
+        if (castIntegerNode == null) {
+            CompilerDirectives.transferToInterpreter();
+            castIntegerNode = adoptChild(CastIntegerNodeFactory.create(null, false, false));
+        }
+    }
+
+    private void initCastLogical() {
+        if (castLogicalNode == null) {
+            CompilerDirectives.transferToInterpreter();
+            castLogicalNode = adoptChild(CastLogicalNodeFactory.create(null, false, false));
+        }
+    }
+
+    private void initCastRaw() {
+        if (castRawNode == null) {
+            CompilerDirectives.transferToInterpreter();
+            castRawNode = adoptChild(CastRawNodeFactory.create(null, false, false));
+        }
+    }
+
+    private RStringVector castStringVector(VirtualFrame frame, RAbstractVector o) {
+        initCastString();
+        return (RStringVector) castStringNode.executeStringVector(frame, o);
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractVector arg, RAbstractVector className) {
+        if (className.getLength() == 0) {
+            return setClass(frame, arg, RNull.instance);
+        }
+        return setClass(frame, arg, castStringVector(frame, className));
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractVector arg, RStringVector className) {
+        if (className.getLength() > 1) {
+            Map<String, Object> attrb = getAttributes(arg);
+            attrb.put(RRuntime.CLASS_ATTR_KEY, className);
+            return resultVector;
+        }
+        return setClassHelper(frame, arg, className.getDataAt(0));
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractVector arg, RNull className) {
+        Map<String, Object> attrb = getAttributes(arg);
+        if (attrb != null) {
+            attrb.remove(RRuntime.CLASS_ATTR_KEY);
+        }
+        return resultVector;
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractLogicalVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_LOGICAL)) {
+            return arg;
+        }
+        return setClassHelper(frame, arg, className);
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractStringVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_CHARACTER)) {
+            return arg;
+        }
+        return setClassHelper(frame, arg, className);
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractComplexVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_COMPLEX)) {
+            return arg;
+        }
+        return setClassHelper(frame, arg, className);
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractDoubleVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_DOUBLE) || className.equals(RRuntime.TYPE_NUMERIC)) {
+            return arg;
+        }
+        return setClassHelper(frame, arg, className);
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractIntVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_INTEGER) || className.equals(RRuntime.TYPE_NUMERIC)) {
+            return arg;
+        }
+        return setClassHelper(frame, arg, className);
+    }
+
+    @Specialization
+    public Object setClass(VirtualFrame frame, RAbstractRawVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_RAW)) {
+            return arg;
+        }
+        return setClassHelper(frame, arg, className);
+    }
+
+    @Specialization
+    public Object setClassHelper(VirtualFrame frame, RAbstractVector arg, String className) {
+        if (className.equals(RRuntime.TYPE_CHARACTER)) {
+            initCastString();
+            return castStringNode.executeString(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_COMPLEX)) {
+            initCastComplex();
+            return castComplexNode.executeComplex(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_DOUBLE)) {
+            initCastDouble();
+            return castDoubleNode.executeDouble(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_INTEGER)) {
+            initCastInteger();
+            return castIntegerNode.executeInt(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_LOGICAL)) {
+            initCastLogical();
+            return castLogicalNode.executeCast(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_RAW)) {
+            initCastRaw();
+            return castRawNode.executeRaw(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_NUMERIC)) {
+            initCastDouble();
+            return castDoubleNode.executeDouble(frame, arg);
+        }
+        if (className.equals(RRuntime.TYPE_MATRIX)) {
+            if (RRuntime.isMatrix(arg)) {
+                return setClass(frame, arg, RNull.instance);
+            }
+            throw RError.getNotMatixUpdateClass(getEncapsulatingSourceSection(), arg.getDimensions().length);
+
+        }
+        if (className.equals(RRuntime.TYPE_ARRAY)) {
+            if (arg.getDimensions().length > 0) {
+                return setClass(frame, arg, RNull.instance);
+            }
+            throw RError.getNotArrayUpdateClass(getEncapsulatingSourceSection());
+        }
+        Map<String, Object> attrb = getAttributes(arg);
+        attrb.put(RRuntime.CLASS_ATTR_KEY, className);
+        return resultVector;
+    }
+
+    private Map<String, Object> getAttributes(RAbstractVector arg) {
+        resultVector = arg.materialize();
+        if (resultVector.isShared()) {
+            resultVector = resultVector.copy();
+        }
+        Map<String, Object> attrb = resultVector.getAttributes();
+        if (attrb == null) {
+            attrb = new LinkedHashMap<>();
+            resultVector.setAttributes((LinkedHashMap<String, Object>) attrb);
+        }
+        return attrb;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
index 6fb71d3af2..79fb89a1be 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
@@ -49,73 +49,125 @@ public abstract class UseMethod extends RBuiltinNode {
         return new RNode[]{ConstantNode.create(RMissing.instance), ConstantNode.create(RMissing.instance)};
     }
 
-    // TODO: Matrix, arrays, integers, strings etc have default class types so there should be a
-// specialization for each one of them.
-
     @Specialization
     public Object useMethod(VirtualFrame frame, String generic, RAbstractVector arg) {
+        if (arg.isObject()) {
+            final Object classAttrb = arg.getAttributes().get(RRuntime.CLASS_ATTR_KEY);
+            if (classAttrb instanceof RStringVector) {
+                RStringVector classNames = (RStringVector) classAttrb;
+                return useMethodHelper(frame, generic, classNames);
+            }
+            return useMethodHelper(frame, generic, (String) classAttrb);
+        } else {
+            return useMethodHelper(frame, generic, arg.getClassHierarchy());
+        }
+    }
 
-        final Map<String, Object> attributes = arg.getAttributes();
-        final Object classAttrb = attributes.get(RRuntime.CLASS_ATTR_KEY);
-        VirtualFrame newFrame = null;
-        if (classAttrb instanceof RStringVector) {
-            RStringVector classNames = (RStringVector) classAttrb;
-            for (int i = 0; i < classNames.getLength() && newFrame == null; ++i) {
-                newFrame = findFunction(classNames.getDataAt(i), generic, frame);
+    /*
+     * If only one argument is passed to UseMethod, the first argument of enclosing function is used
+     * to resolve the generic.
+     */
+    @Specialization
+    public Object useMethod(VirtualFrame frame, String generic, @SuppressWarnings("unused") RMissing arg) {
+        RArguments args = frame.getArguments(RArguments.class);
+        if (args == null || args.getLength() == 0 || args.getArgument(0) == null) {
+            throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, RNull.instance.toString());
+        }
+        Object enclosingArg = args.getArgument(0);
+        if (enclosingArg instanceof Byte) {
+            return useMethod(frame, generic, (byte) enclosingArg);
+        }
+        if (enclosingArg instanceof String) {
+            return useMethod(frame, generic, (String) enclosingArg);
+        }
+        if (enclosingArg instanceof Integer) {
+            return useMethod(frame, generic, (int) enclosingArg);
+        }
+        if (enclosingArg instanceof Double) {
+            return useMethod(frame, generic, (double) enclosingArg);
+        }
+        return useMethod(frame, generic, (RAbstractVector) enclosingArg);
+    }
+
+    @Specialization
+    public Object useMethod(VirtualFrame frame, String generic, byte arg) {
+        return useMethodHelper(frame, generic, RRuntime.TYPE_LOGICAL);
+    }
+
+    @Specialization
+    public Object useMethod(VirtualFrame frame, String generic, String arg) {
+        return useMethodHelper(frame, generic, RRuntime.TYPE_CHARACTER);
+    }
+
+    @Specialization
+    public Object useMethod(VirtualFrame frame, String generic, int arg) {
+        return useMethodHelper(frame, generic, RRuntime.CLASS_INTEGER);
+    }
+
+    @Specialization
+    public Object useMethod(VirtualFrame frame, String generic, double arg) {
+        return useMethodHelper(frame, generic, RRuntime.CLASS_DOUBLE);
+    }
+
+    private Object useMethodHelper(VirtualFrame frame, String generic, String className) {
+        VirtualFrame newFrame = findFunction(className, generic, frame);
+        if (newFrame == null) {
+            newFrame = findFunction(RRuntime.DEFAULT, generic, frame);
+            if (newFrame == null) {
+                throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, className);
             }
         }
-        if (classAttrb instanceof String) {
-            newFrame = findFunction((String) classAttrb, generic, frame);
+        return dispatchMethod(frame, newFrame);
+    }
+
+    private Object useMethodHelper(VirtualFrame frame, String generic, String[] classNames) {
+        VirtualFrame newFrame = null;
+        for (final String className : classNames) {
+            newFrame = findFunction(className, generic, frame);
+            if (newFrame != null) {
+                break;
+            }
         }
         if (newFrame == null) {
             newFrame = findFunction(RRuntime.DEFAULT, generic, frame);
             if (newFrame == null) {
-                throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, classAttrb.toString());
+                throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, Arrays.toString(classNames));
             }
         }
-        // Copy the variables defined(prior to call to UseMethod) in the current frame to the new
-        // frame
-        for (FrameSlot fs : frame.getFrameDescriptor().getSlots()) {
-            switch (fs.getKind()) {
-                case Object:
-                    newFrame.setObject(fs, FrameUtil.getObjectSafe(frame, fs));
-                    break;
-                case Int:
-                    newFrame.setInt(fs, FrameUtil.getIntSafe(frame, fs));
-                    break;
-                case Byte:
-                    newFrame.setByte(fs, FrameUtil.getByteSafe(frame, fs));
-                    break;
-                case Long:
-                    newFrame.setLong(fs, FrameUtil.getLongSafe(frame, fs));
-                    break;
-                case Double:
-                    newFrame.setDouble(fs, FrameUtil.getDoubleSafe(frame, fs));
-                    break;
-                case Float:
-                    newFrame.setFloat(fs, FrameUtil.getFloatSafe(frame, fs));
-                    break;
-                case Boolean:
-                    newFrame.setBoolean(fs, FrameUtil.getBooleanSafe(frame, fs));
-                    break;
+        return dispatchMethod(frame, newFrame);
+    }
+
+    private Object useMethodHelper(VirtualFrame frame, String generic, List<String> classNames) {
+        VirtualFrame newFrame = null;
+        for (final String className : classNames) {
+            newFrame = findFunction(className, generic, frame);
+            if (newFrame != null) {
+                break;
             }
         }
-        return funcDefnNode.execute(newFrame);
+        if (newFrame == null) {
+            newFrame = findFunction(RRuntime.DEFAULT, generic, frame);
+            if (newFrame == null) {
+                throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, classNames.toString());
+            }
+        }
+        return dispatchMethod(frame, newFrame);
     }
 
-    /*
-     * If only one argument is passed to UseMethod, the first argument of enclosing function is used
-     * to resolve the generic.
-     */
-    @Specialization
-    public Object useMethod(VirtualFrame frame, String generic, RMissing arg) {
-        RAbstractVector enclosingArg = (RAbstractVector) frame.getArguments(RArguments.class).getArgument(0);
-        return useMethod(frame, generic, enclosingArg);
+    private Object useMethodHelper(VirtualFrame frame, String generic, RStringVector classNames) {
+        VirtualFrame newFrame = null;
+        for (int i = 0; i < classNames.getLength() && newFrame == null; ++i) {
+            newFrame = findFunction(classNames.getDataAt(i), generic, frame);
+        }
+        if (newFrame == null) {
+            newFrame = findFunction(RRuntime.DEFAULT, generic, frame);
+            if (newFrame == null) {
+                throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, classNames.toString());
+            }
+        }
+        return dispatchMethod(frame, newFrame);
     }
 
-    /*
-     * @Specialization public Object useMethod(VirtualFrame frame, String generic, RDouble arg) { }
-     */
     private VirtualFrame findFunction(final String className, final String generic, VirtualFrame frame) {
         StringBuilder sbFuncName = new StringBuilder(generic);
         sbFuncName.append(".");
@@ -142,4 +194,37 @@ public abstract class UseMethod extends RBuiltinNode {
         }
         return null;
     }
+
+    private Object dispatchMethod(VirtualFrame frame, VirtualFrame newFrame) {
+
+        // Copy the variables defined(prior to call to UseMethod) in the current frame to the new
+        // frame
+        for (FrameSlot fs : frame.getFrameDescriptor().getSlots()) {
+            switch (fs.getKind()) {
+                case Object:
+                    newFrame.setObject(fs, FrameUtil.getObjectSafe(frame, fs));
+                    break;
+                case Int:
+                    newFrame.setInt(fs, FrameUtil.getIntSafe(frame, fs));
+                    break;
+                case Byte:
+                    newFrame.setByte(fs, FrameUtil.getByteSafe(frame, fs));
+                    break;
+                case Long:
+                    newFrame.setLong(fs, FrameUtil.getLongSafe(frame, fs));
+                    break;
+                case Double:
+                    newFrame.setDouble(fs, FrameUtil.getDoubleSafe(frame, fs));
+                    break;
+                case Float:
+                    newFrame.setFloat(fs, FrameUtil.getFloatSafe(frame, fs));
+                    break;
+                case Boolean:
+                    newFrame.setBoolean(fs, FrameUtil.getBooleanSafe(frame, fs));
+                    break;
+            }
+        }
+        return funcDefnNode.execute(newFrame);
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 7615ab94dd..9d9bb5ea6d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -208,7 +208,11 @@ public abstract class RError extends RuntimeException {
     public static final String FORMAL_MATCHED_MULTIPLE = "formal argument \"%s\" matched by multiple actual arguments";
     public static final String ARGUMENT_MATCHES_MULTIPLE = "argument %d matches multiple formal arguments";
     public static final String ARGUMENT_EMPTY = "argument %d is empty";
-    public static final String REPEATED_FORMAL = "repeated formal argument '%s'"; // not exactly
+    public static final String REPEATED_FORMAL = "repeated formal argument '%s'";
+    public static final String NOT_A_MATRIX_UPDATE_CLASS = "invalid to set the class to matrix unless the dimension attribute is of length 2 (was '%d')";
+    public static final String NOT_ARRAY_UPDATE_CLASS = "cannot set class to \"array\" unless the dimension attribute has length > 0";
+    public static final String SET_INVALID_CLASS_ATTR = "attempt to set invalid 'class' attribute";
+    // not exactly
     // GNU-R message
     public static final String DOTS_BOUNDS = "The ... list does not contain %s elements";
     public static final String REFERENCE_NONEXISTENT = "reference to non-existent argument %d";
@@ -1906,6 +1910,18 @@ public abstract class RError extends RuntimeException {
         return getGenericError(ast, stringFormat(RError.RECURSIVE_INDEXING_FAILED, level));
     }
 
+    public static RError getNotMatixUpdateClass(SourceSection ast, int dim) {
+        return getGenericError(ast, stringFormat(RError.NOT_A_MATRIX_UPDATE_CLASS, dim));
+    }
+
+    public static RError getNotArrayUpdateClass(SourceSection ast) {
+        return getGenericError(ast, RError.NOT_ARRAY_UPDATE_CLASS);
+    }
+
+    public static RError getInvalidClassAttr(SourceSection ast) {
+        return getGenericError(ast, RError.SET_INVALID_CLASS_ATTR);
+    }
+
     @SlowPath
     private static String stringFormat(String format, Object... args) {
         return String.format(format, args);
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 2a2af7f760..f6c74006b7 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
@@ -83,6 +83,9 @@ public class RRuntime {
     public static final String TYPE_CHARACTER = new String("character");
     public static final String TYPE_LOGICAL = new String("logical");
     public static final String TYPE_RAW = new String("raw");
+    public static final String TYPE_MATRIX = new String("matrix");
+    public static final String TYPE_ARRAY = new String("array");
+    public static final String TYPE_LIST = new String("list");
 
     public static final String TYPE_NUMERIC_CAP = new String("Numeric");
     public static final String TYPE_INTEGER_CAP = new String("Integer");
@@ -107,6 +110,9 @@ public class RRuntime {
 
     public static final String CLASS_ATTR_KEY = "class";
 
+    public static final String[] CLASS_INTEGER = new String[]{TYPE_INTEGER, TYPE_NUMERIC};
+    public static final String[] CLASS_DOUBLE = new String[]{TYPE_DOUBLE, TYPE_NUMERIC};
+
     public static RComplex createComplexNA() {
         return RDataFactory.createComplex(DOUBLE_NA, 0.0);
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java
index 1995e5406c..ef2f71108e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java
@@ -164,4 +164,10 @@ public final class RComplexVector extends RVector implements RAbstractComplexVec
         return getDataAt(index);
     }
 
+    @Override
+    public List<String> getClassHierarchy() {
+        List<String> klass = super.getClassHierarchy();
+        klass.add(RRuntime.TYPE_COMPLEX);
+        return klass;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleSequence.java
index e94b2c9a53..718e1b933a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleSequence.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleSequence.java
@@ -30,6 +30,13 @@ public final class RDoubleSequence extends RSequence implements RAbstractDoubleV
     private final double start;
     private final double stride;
 
+    private static final List<String> klass;
+    static {
+        klass = new ArrayList<>();
+        klass.add(RRuntime.TYPE_DOUBLE);
+        klass.add(RRuntime.TYPE_NUMERIC);
+    }
+
     RDoubleSequence(double start, double stride, int length) {
         super(length);
         assert length > 0;
@@ -91,4 +98,9 @@ public final class RDoubleSequence extends RSequence implements RAbstractDoubleV
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    @Override
+    public List<String> getClassHierarchy() {
+        return klass;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java
index 3c42287a00..1cfe02a8d3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java
@@ -174,4 +174,10 @@ public final class RDoubleVector extends RVector implements RAbstractDoubleVecto
         this.data = newData;
     }
 
+    @Override
+    public List<String> getClassHierarchy() {
+        final List<String> classHr = super.getClassHierarchy();
+        classHr.addAll(Arrays.asList(RRuntime.CLASS_DOUBLE));
+        return classHr;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntSequence.java
index 6ed1b9f30f..b97f889f4b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntSequence.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntSequence.java
@@ -30,6 +30,13 @@ public final class RIntSequence extends RSequence implements RAbstractIntVector
     private final int start;
     private final int stride;
 
+    private static final List<String> klass;
+    static {
+        klass = new ArrayList<>();
+        klass.add(RRuntime.TYPE_INTEGER);
+        klass.add(RRuntime.TYPE_NUMERIC);
+    }
+
     RIntSequence(int start, int stride, int length) {
         super(length);
         assert length > 0;
@@ -93,4 +100,8 @@ public final class RIntSequence extends RSequence implements RAbstractIntVector
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    public List<String> getClassHierarchy() {
+        return klass;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java
index 523fcedcc1..3168c7ae71 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java
@@ -157,4 +157,11 @@ public final class RIntVector extends RVector implements RAbstractIntVector {
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    @Override
+    public List<String> getClassHierarchy() {
+        final List<String> classHr = super.getClassHierarchy();
+        classHr.addAll(Arrays.asList(RRuntime.CLASS_INTEGER));
+        return classHr;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
index 12c0c8ec94..98442acf93 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
@@ -163,4 +163,11 @@ public final class RList extends RVector implements RAbstractVector {
     protected void resizeInternal(int size) {
         this.data = createResizedData(size, true);
     }
+
+    @Override
+    public List<String> getClassHierarchy() {
+        List<String> klass = super.getClassHierarchy();
+        klass.add(RRuntime.TYPE_LIST);
+        return klass;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java
index 9ebd72632a..12f69444e4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java
@@ -148,4 +148,11 @@ public final class RLogicalVector extends RVector implements RAbstractLogicalVec
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    @Override
+    public List<String> getClassHierarchy() {
+        final List<String> classHr = super.getClassHierarchy();
+        classHr.add(RRuntime.TYPE_LOGICAL);
+        return classHr;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java
index 6cce99315a..12781ebf97 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java
@@ -143,4 +143,11 @@ public final class RRawVector extends RVector implements RAbstractRawVector {
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    @Override
+    public List<String> getClassHierarchy() {
+        final List<String> classHr = super.getClassHierarchy();
+        classHr.add(RRuntime.TYPE_RAW);
+        return classHr;
+    }
 }
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 410fbb261d..5d786a08b2 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
@@ -84,4 +84,12 @@ public abstract class RSequence extends RBounded implements RAbstractVector {
     public boolean isMatrix() {
         return false;
     }
+
+    public boolean isArray() {
+        return false;
+    }
+
+    public boolean isObject() {
+        return false;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
index a5bb3b6340..b8f903dcc1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java
@@ -148,4 +148,11 @@ public final class RStringVector extends RVector implements RAbstractStringVecto
     public Object getDataAtAsObject(int index) {
         return getDataAt(index);
     }
+
+    @Override
+    public List<String> getClassHierarchy() {
+        final List<String> classHr = super.getClassHierarchy();
+        classHr.add(RRuntime.TYPE_CHARACTER);
+        return classHr;
+    }
 }
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 6a3df393d4..9100d2d063 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
@@ -212,6 +212,10 @@ public abstract class RVector extends RBounded implements RAbstractVector {
         return matrixDimension != 0;
     }
 
+    public final boolean isArray() {
+        return dimensions != null && dimensions.length > 0;
+    }
+
     public final int[] getDimensions() {
         if (hasDimensions()) {
             return Arrays.copyOf(dimensions, dimensions.length);
@@ -382,4 +386,20 @@ public abstract class RVector extends RBounded implements RAbstractVector {
             }
         }
     }
+
+    public boolean isObject() {
+        return (this.attributes != null && this.attributes.get(RRuntime.CLASS_ATTR_KEY) != null) ? true : false;
+    }
+
+    // As shape of the vector may change at run-time we need to compute
+    // class hierarchy on the fly.
+    public List<String> getClassHierarchy() {
+        List<String> klass = new ArrayList<>();
+        if (this.isMatrix()) {
+            klass.add(RRuntime.TYPE_MATRIX);
+        } else if (this.isArray()) {
+            klass.add(RRuntime.TYPE_ARRAY);
+        }
+        return klass;
+    }
 }
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 f505beffb5..049b8db2ed 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
@@ -80,4 +80,15 @@ public abstract class RToVectorClosure implements RAbstractVector {
         return vector.isMatrix();
     }
 
+    public boolean isArray() {
+        return vector.isArray();
+    }
+
+    public List<String> getClassHierarchy() {
+        return vector.getClassHierarchy();
+    }
+
+    public boolean isObject() {
+        return vector.isObject();
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
index 4b59ab5cc0..5c72de906b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
@@ -56,4 +56,10 @@ public interface RAbstractVector {
     RList getDimNames();
 
     boolean isMatrix();
+
+    boolean isArray();
+
+    boolean isObject();
+
+    List<String> getClassHierarchy();
 }
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index c45f835b92..59914d2e24 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -1,6 +1,8 @@
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Base.r,gnu_r.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Covcor.java,gnu_r.copyright
+com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/Rnorm.java,gnu_r.copyright
+com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java,purdue.copyright
 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java,purdue.copyright
 com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParseException.java,purdue.copyright
 com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParseUtil.java,purdue.copyright
-- 
GitLab