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 3300be54fd584fcc6f016fdae4818ef2cdbfeeb4..c39ea95a71b90e52551afad66dc6f8bb36019e5b 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(Getwd.class);
         load(GlobalEnv.class);
         load(Gregexpr.class);
@@ -172,12 +173,14 @@ 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);
         load(UpdateLength.class);
         load(UpdateNames.class);
         load(UpperTri.class);
+        load(UseMethod.class);
         load(Which.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 0000000000000000000000000000000000000000..edb10882611cd2c0a0d25c5018dc077609e03c65
--- /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 ce5226b885232ce5999263b61b2f976441bba05f..33e2ac4c34aff5d6c39fb7464c22269902c9db82 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
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import java.util.*;
+
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
@@ -38,10 +40,8 @@ public abstract class IsObject extends RBuiltinNode {
     }
 
     @Specialization
-    @SuppressWarnings("unused")
     public byte isObject(RAbstractVector arg) {
-        // FIXME return the result of class attribute presence check (once supported)
-        return RRuntime.LOGICAL_FALSE;
+        Map<String, Object> attributes = arg.getAttributes();
+        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 ce0694b5123ffb08e781bfde5e04869b1aca7922..f55139c0bb3d6fe2766fede9aea4d43a0c3270b1 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 0000000000000000000000000000000000000000..9d51e8beef0fc92c2bc4b647bbe7df0225f97b6e
--- /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
new file mode 100644
index 0000000000000000000000000000000000000000..79fb89a1be0d6d6f5e06aa91aae471e974303581
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/base/UseMethod.java
@@ -0,0 +1,230 @@
+/*
+ * 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.CompilerDirectives.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.*;
+import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.r.nodes.access.*;
+import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.nodes.function.*;
+import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.model.*;
+
+@RBuiltin(value = "UseMethod")
+public abstract class UseMethod extends RBuiltinNode {
+
+    /*
+     * TODO: If more than two parameters are passed to UseMethod the extra parameters are ignored
+     * and a warning is generated.
+     */
+    private static final Object[] PARAMETER_NAMES = new Object[]{"generic", "object"};
+
+    @Child protected ReadVariableNode lookup;
+    @CompilationFinal protected String lastFun;
+
+    private FunctionDefinitionNode funcDefnNode;
+
+    @Override
+    public Object[] getParameterNames() {
+        return PARAMETER_NAMES;
+    }
+
+    @Override
+    public RNode[] getArguments() {
+        return new RNode[]{ConstantNode.create(RMissing.instance), ConstantNode.create(RMissing.instance)};
+    }
+
+    @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());
+        }
+    }
+
+    /*
+     * 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);
+            }
+        }
+        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, Arrays.toString(classNames));
+            }
+        }
+        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;
+            }
+        }
+        if (newFrame == null) {
+            newFrame = findFunction(RRuntime.DEFAULT, generic, frame);
+            if (newFrame == null) {
+                throw RError.getUnknownFunctionUseMethod(getEncapsulatingSourceSection(), generic, classNames.toString());
+            }
+        }
+        return dispatchMethod(frame, newFrame);
+    }
+
+    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);
+    }
+
+    private VirtualFrame findFunction(final String className, final String generic, VirtualFrame frame) {
+        StringBuilder sbFuncName = new StringBuilder(generic);
+        sbFuncName.append(".");
+        sbFuncName.append(className);
+        final String funcName = RRuntime.toString(sbFuncName);
+        if (lookup == null || !funcName.equals(lastFun)) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            lastFun = funcName;
+            ReadVariableNode rvn = ReadVariableNode.create(funcName, true, false);
+            lookup = lookup == null ? adoptChild(rvn) : lookup.replace(rvn);
+        }
+        Object func = null;
+        try {
+            func = lookup.execute((VirtualFrame) frame.getCaller().unpack());
+        } catch (RError r) {
+            return null;
+        }
+        if (func != null && func instanceof RFunction) {
+            final RFunction targetFunction = (RFunction) func;
+            funcDefnNode = (FunctionDefinitionNode) (((DefaultCallTarget) targetFunction.getTarget()).getRootNode());
+            final RArguments currentArguments = frame.getArguments(RArguments.class);
+            final RArguments newArguments = RArguments.create(targetFunction, targetFunction.getEnclosingFrame(), currentArguments.getArgumentsArray(), currentArguments.getNames());
+            return Truffle.getRuntime().createVirtualFrame(frame.getCaller(), newArguments, frame.getFrameDescriptor().copy());
+        }
+        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 241f302a1e2d67a4cd99d2090a3e856bfb058e2a..e9aebeae9f75f9438550720ef9425079786fa3cb 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
@@ -161,6 +161,7 @@ public abstract class RError extends RuntimeException {
     public static final String ARGUMENT_NOT_MATCH = "supplied argument name '%s' does not match '%s'";
     public static final String ARGUMENT_MISSING = "argument '%s' is missing, with no default";
     public static final String UNKNOWN_FUNCTION = "could not find function '%s'";
+    public static final String UNKNOWN_FUNCTION_USE_METHOD = "Error in UseMethod('%s') : \n no applicable method for '%s' applied to an object of class '%s'";
     public static final String UNKNOWN_OBJECT = "object '%s' not found";
     public static final String INVALID_ARGUMENT = "invalid '%s' argument";
     public static final String INVALID_SUBSCRIPT_TYPE = "invalid subscript type '%s'";
@@ -209,7 +210,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";
@@ -1674,6 +1679,10 @@ public abstract class RError extends RuntimeException {
         return getGenericError(ast, stringFormat(RError.UNKNOWN_FUNCTION, variable));
     }
 
+    public static RError getUnknownFunctionUseMethod(SourceSection ast, String function, String classVector) {
+        return getGenericError(ast, stringFormat(RError.UNKNOWN_FUNCTION_USE_METHOD, function, function, classVector));
+    }
+
     public static RError getInvalidArgument(SourceSection ast, String str) {
         return getGenericError(ast, stringFormat(RError.INVALID_ARGUMENT, str));
     }
@@ -1939,6 +1948,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 c40baa7802d70d3754e8dcbd0dbafcfdd6858784..685610d05fe06d300a5356c0fd9578891f8f6b00 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
@@ -86,6 +86,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");
@@ -98,6 +101,7 @@ public class RRuntime {
     public static final REnvironment GLOBAL_ENV = RGlobalEnvironment.instance;
 
     public static final String[] STRING_ARRAY_SENTINEL = new String[0];
+    public static final String DEFAULT = "default";
 
     public static final String NAMES_ATTR_KEY = new String("names");
     public static final String NAMES_ATTR_EMPTY_VALUE = "";
@@ -107,6 +111,11 @@ public class RRuntime {
     public static final String DIMNAMES_ATTR_KEY = "dimnames";
     public static final String DIMNAMES_LIST_ELEMENT_NAME_PREFIX = "$dimnames";
 
+    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(COMPLEX_NA_REAL_PART, COMPLEX_NA_IMAGINARY_PART);
     }
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 1995e5406cb74e161aaa7539903877a9209e27ef..ef2f71108edb4ba3419081b265e58c12561ee4e6 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 e94b2c9a537722a6702fa7f250b2f8c659275366..11bf2ab6908fa1357528ba03674d2c388f9faf3a 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
@@ -22,7 +22,10 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
+import java.util.*;
+
 import com.oracle.truffle.api.CompilerDirectives.SlowPath;
+import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 public final class RDoubleSequence extends RSequence implements RAbstractDoubleVector {
@@ -30,6 +33,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 +101,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 3c42287a008ca84ff1108101b5f1cd71557e717c..1cfe02a8d361a525b0b0288bdcc6f095b6f16ef3 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 6ed1b9f30f36c23837d94909a76d40117dcfa5c1..02e5c74d3e1d6771659321981cdbe70fed23eae8 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
@@ -22,7 +22,10 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
+import java.util.*;
+
 import com.oracle.truffle.api.CompilerDirectives.SlowPath;
+import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
 public final class RIntSequence extends RSequence implements RAbstractIntVector {
@@ -30,6 +33,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 +103,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 523fcedcc1ea3890675cbfa788b2af1f11840840..3168c7ae7132eece9beebf83f28c6d44b73cf02c 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 12c0c8ec94d62b1568a3eb74ccf38c1ae5223e7d..98442acf93b003003a56fd561b2c943897fcf77e 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 9ebd72632af08b1a3c30001f4ded789ba75ce430..12f69444e41f8450e6fadd0fbdf5db71fc6c03db 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 6cce99315acd84533603b4611aa4b56807b3eb2a..12781ebf97aa61acdb1f44a978af613e30e99204 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 410fbb261dfbb4bff91440ebb178c177f18d2485..5d786a08b2cf1ebf707f26c161bcaf457d89312a 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 a5bb3b63400845268a5c648d055c7bb3d8eb5865..b8f903dcc1e13eb160601b2652a972473ca1fe99 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 6a3df393d40fdcad710bc20a313a5b59a7ea64f7..9100d2d0639cb6b9ce0d1f3b4e81837235c5cbc3 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 f505beffb58f4e9690e2739df2ddd33f1396024e..049b8db2ed129c9b0f8095c64fa6a9490295f52d 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 4b59ab5cc0864ad13231b461180337fd2d168e2a..5c72de906b6d5d734d0c003ca4ba0028c6780a3d 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/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 e49132203186ea6bcfedf18ecb0089a50e43394c..7fa74d0b081e25d3dea54076205fead80abcb4f8 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
@@ -6721,6 +6721,21 @@ $<NA>
 #{ z <- c(a=1, b=2) ; names(z) <- NULL ; z }
 [1] 1 2
 
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodEnclFuncArgs
+#{f <- function(x,y,z){ UseMethod("f"); }; f.second <- function(x,y,z){cat("f second",x,y,z)}; obj <-1; attr(obj,"class") <- "second"; arg2=2; arg3=3; f(obj,arg2,arg3);}
+f second 1 2 3
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodLocalVars
+#{f <- function(x){ y<-2;locFun <- function(){cat("local")}; UseMethod("f"); }; f.second <- function(x){cat("f second",x);locFun();}; obj <-1; attr(obj,"class")  <- "second"; f(obj);}
+f second 1local
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodNested
+#{f <- function(x){g<- function(x){ h<- function(x){ UseMethod("f");}; h(x)}; g(x) }; f.second <- function(x){cat("f second",x);}; obj <-1; attr(obj,"class")  <- "second"; f(obj);}
+f second 1
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodOneArg
+#{f <- function(x){ UseMethod("f"); };f.first <- function(x){cat("f first",x)}; f.second <- function(x){cat("f second",x)}; obj <-1; attr(obj,"class")  <- "first"; f(obj); attr(obj,"class")  <- "second"; f(obj);}
+f first 1f second 1
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUseMethodSimple
+#{f <- function(x){ UseMethod("f",x); };f.first <- function(x){cat("f first",x)};f.second <- function(x){cat("f second",x)};obj <-1;attr(obj,"class")  <- "first";f(obj);attr(obj,"class")  <- "second";f(obj)}
+f first 1f second 1
 ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testWhich
 #{ which(c(TRUE, FALSE, NA, TRUE)) }
 [1] 1 4
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java
index 838ee2b842438683403cd064997efaa8d2e844de..61d08dd0d6ef5a4b8542f5594fad134dbb1099b9 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/all/AllTests.java
@@ -10438,6 +10438,36 @@ public class AllTests extends TestBase {
         assertEval("{ upper.tri(1:3, diag=FALSE) }");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testUseMethodEnclFuncArgs_c699286a5e7dd6ca4c46b1245a1f633e() {
+        assertEval("{f <- function(x,y,z){ UseMethod(\"f\"); }; f.second <- function(x,y,z){cat(\"f second\",x,y,z)}; obj <-1; attr(obj,\"class\") <- \"second\"; arg2=2; arg3=3; f(obj,arg2,arg3);}");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testUseMethodIgnore_1af23cb23456744d7e6a4cb93888e9a3() {
+        assertEval("{f <- function(x){ UseMethod(\"f\");cat(\"This should not be executed\"); }; f.second <- function(x){cat(\"f second\",x);}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testUseMethodLocalVars_cd724107886a7c9d25ae3b6aad713cb6() {
+        assertEval("{f <- function(x){ y<-2;locFun <- function(){cat(\"local\")}; UseMethod(\"f\"); }; f.second <- function(x){cat(\"f second\",x);locFun();}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testUseMethodNested_d689820491ffcbc9ddb83012801bd243() {
+        assertEval("{f <- function(x){g<- function(x){ h<- function(x){ UseMethod(\"f\");}; h(x)}; g(x) }; f.second <- function(x){cat(\"f second\",x);}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testUseMethodOneArg_fce364ef2dfa8e366da5615934951253() {
+        assertEval("{f <- function(x){ UseMethod(\"f\"); };f.first <- function(x){cat(\"f first\",x)}; f.second <- function(x){cat(\"f second\",x)}; obj <-1; attr(obj,\"class\")  <- \"first\"; f(obj); attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void TestSimpleBuiltins_testUseMethodSimple_f4ab882034aa9d9c9d106566155c9a1d() {
+        assertEval("{f <- function(x){ UseMethod(\"f\",x); };f.first <- function(x){cat(\"f first\",x)};f.second <- function(x){cat(\"f second\",x)};obj <-1;attr(obj,\"class\")  <- \"first\";f(obj);attr(obj,\"class\")  <- \"second\";f(obj)}");
+    }
+
     @Test
     public void TestSimpleBuiltins_testWhich_abb40fde89cc0dfbb69ec73c399e9ee0() {
         assertEval("{ which(c(TRUE, FALSE, NA, TRUE)) }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java
index d151e57105de2f60cbe69df8dd331dc7aa2aee7f..4480a0c746a9834c664d31f1af15b345bdfddb5d 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java
@@ -3438,6 +3438,11 @@ public class FailingTests extends TestBase {
         assertEval("{ upper.tri(1:3, diag=FALSE) }");
     }
 
+    @Ignore
+    public void TestSimpleBuiltins_testUseMethodIgnore_1af23cb23456744d7e6a4cb93888e9a3() {
+        assertEval("{f <- function(x){ UseMethod(\"f\");cat(\"This should not be executed\"); }; f.second <- function(x){cat(\"f second\",x);}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
     @Ignore
     public void TestSimpleBuiltins_testWhichIgnore_6d01b8ef11e5cdf979ca7122cd3de717() {
         assertEval("{ which(c(a=TRUE,b=FALSE,c=TRUE)) }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java
index 17cc3b441f0c95ede4bed04d082ad2bc94b53965..45256c9accfb0cbf98896407dd926b5a55ac1a54 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleBuiltins.java
@@ -2348,4 +2348,48 @@ public class TestSimpleBuiltins extends TestBase {
         assertEvalWarning("{ rm(\"ieps\") }");
         assertEval("{ x <- 200 ; rm(\"x\") }");
     }
+
+    @Test
+    public void testUseMethodSimple() {
+        // Basic UseMethod
+        assertEval("{f <- function(x){ UseMethod(\"f\",x); };" + "f.first <- function(x){cat(\"f first\",x)};" + "f.second <- function(x){cat(\"f second\",x)};" + "obj <-1;"
+                        + "attr(obj,\"class\")  <- \"first\";" + "f(obj);" + "attr(obj,\"class\")  <- \"second\";" + "f(obj)}");
+    }
+
+    @Test
+    public void testUseMethodOneArg() {
+        // If only one argument is passed to UseMethod(), the call should
+        // be resolved based on first argument to enclosing function.
+        assertEval("{f <- function(x){ UseMethod(\"f\"); };f.first <- function(x){cat(\"f first\",x)}; f.second <- function(x){cat(\"f second\",x)}; obj <-1; attr(obj,\"class\")  <- \"first\"; f(obj); attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void testUseMethodLocalVars() {
+        // The variables defined before call to UseMethod should be
+        // accessible to target function.
+        assertEval("{f <- function(x){ y<-2;locFun <- function(){cat(\"local\")}; UseMethod(\"f\"); }; f.second <- function(x){cat(\"f second\",x);locFun();}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void testUseMethodNested() {
+        // The UseMethod call can be nested deep compared to where target is
+        // defined.
+        assertEval("{f <- function(x){g<- function(x){ h<- function(x){ UseMethod(\"f\");}; h(x)}; g(x) }; f.second <- function(x){cat(\"f second\",x);}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
+
+    @Test
+    public void testUseMethodEnclFuncArgs() {
+        // All the argument passed to the caller of UseMethod() should be
+        // accessible to the target method.
+        assertEval("{f <- function(x,y,z){ UseMethod(\"f\"); }; f.second <- function(x,y,z){cat(\"f second\",x,y,z)}; obj <-1; attr(obj,\"class\") <- \"second\"; arg2=2; arg3=3; f(obj,arg2,arg3);}");
+
+    }
+
+    @Test
+    @Ignore
+    public void testUseMethodIgnore() {
+        // TODO
+        // All the statements after UseMethod() call should get ignored.
+        assertEval("{f <- function(x){ UseMethod(\"f\");cat(\"This should not be executed\"); }; f.second <- function(x){cat(\"f second\",x);}; obj <-1; attr(obj,\"class\")  <- \"second\"; f(obj);}");
+    }
 }
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 74cec1009b97151591c602cca2d8ce2e18465d33..59914d2e24de2e70437bc981e62fa5e89bce792f 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -1,6 +1,9 @@
 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
 com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g,purdue.copyright