From 2deacdd081d8c0a0368cc0c14b8a1d3409f128d1 Mon Sep 17 00:00:00 2001
From: Adam Welc <adam.welc@oracle.com>
Date: Fri, 30 Jan 2015 17:28:58 -0800
Subject: [PATCH] Added support for calling NextMethod explicitly which enabled
 accessing factors via access methods defined in R.

---
 .../truffle/r/nodes/builtin/base/DoCall.java  |  4 +-
 .../builtin/base/InfixEmulationFunctions.java | 62 ++++++++++++++++++
 .../r/nodes/builtin/base/NextMethod.java      | 19 ++++--
 .../truffle/r/nodes/builtin/base/R/factor.R   | 40 ++++++------
 .../truffle/r/nodes/builtin/base/R/sort.R     |  4 +-
 .../r/nodes/function/ArgumentMatcher.java     | 29 +++++----
 .../r/nodes/function/DispatchedCallNode.java  | 17 +++--
 .../nodes/function/GroupDispatchCallNode.java |  4 +-
 .../function/NextMethodDispatchNode.java      | 64 ++++++++++++++++---
 .../r/nodes/function/S3DispatchNode.java      |  5 +-
 .../nodes/function/UseMethodDispatchNode.java |  4 +-
 .../r/nodes/unary/CastToVectorNode.java       |  8 ++-
 .../oracle/truffle/r/runtime/RArguments.java  | 41 +++++++++---
 .../truffle/r/test/ExpectedTestOutput.test    | 39 +++++++++++
 .../oracle/truffle/r/test/all/AllTests.java   | 40 ++++++++++++
 .../r/test/simple/TestSimpleVectors.java      | 12 ++++
 16 files changed, 323 insertions(+), 69 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
index 127a3f25a9..bd46ca59d2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -69,7 +69,7 @@ public abstract class DoCall extends RBuiltinNode {
         Object n = argsAsList.getNames();
         String[] argNames = n == RNull.instance ? null : ((RStringVector) n).getDataNonShared();
         EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, argNames);
-        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, getEncapsulatingSourceSection(), promiseHelper);
+        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, getEncapsulatingSourceSection(), promiseHelper, false);
         Object[] callArgs = RArguments.create(func, callCache.getSourceSection(), RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
         RArguments.setIsIrregular(callArgs, true);
         return callCache.execute(frame, func.getTarget(), callArgs);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
index 3027f3f2bc..203a6ba7b7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java
@@ -34,6 +34,8 @@ import com.oracle.truffle.r.nodes.access.array.ArrayPositionCast.*;
 import com.oracle.truffle.r.nodes.access.array.ArrayPositionCastNodeGen.*;
 import com.oracle.truffle.r.nodes.access.array.read.*;
 import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctionsFactory.PromiseEvaluatorNodeGen;
+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.*;
@@ -99,10 +101,70 @@ public class InfixEmulationFunctions {
 
     }
 
+    @NodeChild(value = "op")
+    protected abstract static class PromiseEvaluator extends RNode {
+
+        protected abstract Object execute(VirtualFrame frame, Object op);
+
+        @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode();
+        @Child private PromiseEvaluator evalRecursive;
+
+        protected Object evalRecursive(VirtualFrame frame, Object op) {
+            if (evalRecursive == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                evalRecursive = insert(PromiseEvaluatorNodeGen.create(null));
+            }
+            return evalRecursive.execute(frame, op);
+        }
+
+        @Specialization
+        protected Object eval(VirtualFrame frame, RPromise p) {
+            return promiseHelper.evaluate(frame, p);
+        }
+
+        @Specialization
+        protected RAbstractVector eval(RAbstractVector op) {
+            return op;
+        }
+
+        @ExplodeLoop
+        @Specialization(guards = "!argsEmpty")
+        protected RArgsValuesAndNames eval(VirtualFrame frame, RArgsValuesAndNames args) {
+            Object[] values = args.getValues();
+            for (int i = 0; i < values.length; i++) {
+                values[i] = evalRecursive(frame, values[i]);
+            }
+            return args;
+        }
+
+        @Specialization(guards = "argsEmpty")
+        protected RArgsValuesAndNames evalEmpty(RArgsValuesAndNames args) {
+            return args;
+        }
+
+        @Fallback
+        protected Object eval(Object op) {
+            return op;
+        }
+
+        protected boolean argsEmpty(RArgsValuesAndNames args) {
+            return args.length() == 0;
+        }
+
+    }
+
     public abstract static class AccessArrayBuiltin extends RBuiltinNode {
         @Child private AccessArrayNode accessNode;
         @Child private AccessPositions positions;
 
+        @CreateCast("arguments")
+        public RNode[] castArguments(RNode[] arguments) {
+            for (int i = 0; i < arguments.length; i++) {
+                arguments[i] = PromiseEvaluatorNodeGen.create(arguments[i]);
+            }
+            return arguments;
+        }
+
         @ExplodeLoop
         protected Object access(VirtualFrame frame, Object vector, byte exact, RArgsValuesAndNames inds, Object dropDim, boolean isSubset) {
             if (accessNode == null || positions.getLength() != inds.length()) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java
index d759f9128a..fa5d032e6c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NextMethod.java
@@ -33,6 +33,7 @@ public abstract class NextMethod extends RBuiltinNode {
     @Child private DispatchedCallNode dispatchedCallNode;
     @Child private ReadVariableNode rvnClass;
     @Child private ReadVariableNode rvnGeneric;
+    @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode();
     @CompilationFinal private String lastGenericName;
 
     private final BranchProfile errorProfile = BranchProfile.create();
@@ -46,14 +47,19 @@ public abstract class NextMethod extends RBuiltinNode {
     protected Object nextMethod(VirtualFrame frame, String genericMethod, @SuppressWarnings("unused") Object obj, Object[] args) {
         controlVisibility();
         final RStringVector type = readType(frame);
-        final String genericName = readGenericName(frame, genericMethod);
+        final String genericName = genericMethod == null ? readGenericName(frame, genericMethod) : genericMethod;
         if (genericName == null) {
             errorProfile.enter();
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.GEN_FUNCTION_NOT_SPECIFIED);
         }
         if (dispatchedCallNode == null || !lastGenericName.equals(genericName)) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            DispatchedCallNode dcn = DispatchedCallNode.create(genericName, RRuntime.NEXT_METHOD, args);
+            RFunction enclosingFunction = RArguments.getFunction(frame);
+            String enclosingFunctionName = null;
+            if (!RArguments.hasS3Args(frame)) {
+                enclosingFunctionName = enclosingFunction.getRootNode().toString();
+            }
+            DispatchedCallNode dcn = DispatchedCallNode.create(genericName, enclosingFunctionName, RRuntime.NEXT_METHOD, args);
             dispatchedCallNode = dispatchedCallNode == null ? insert(dcn) : dispatchedCallNode.replace(dcn);
             lastGenericName = genericName;
         }
@@ -73,11 +79,16 @@ public abstract class NextMethod extends RBuiltinNode {
     }
 
     private RStringVector getAlternateClassHr(VirtualFrame frame) {
-        if (RArguments.getArgumentsLength(frame) == 0 || RArguments.getArgument(frame, 0) == null || !(RArguments.getArgument(frame, 0) instanceof RAbstractVector)) {
+        if (RArguments.getArgumentsLength(frame) == 0 || RArguments.getArgument(frame, 0) == null ||
+                        (!(RArguments.getArgument(frame, 0) instanceof RAbstractVector) && !(RArguments.getArgument(frame, 0) instanceof RPromise))) {
             errorProfile.enter();
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.OBJECT_NOT_SPECIFIED);
         }
-        RAbstractVector enclosingArg = (RAbstractVector) RArguments.getArgument(frame, 0);
+        Object arg = RArguments.getArgument(frame, 0);
+        if (arg instanceof RPromise) {
+            arg = promiseHelper.evaluate(frame, (RPromise) arg);
+        }
+        RAbstractContainer enclosingArg = (RAbstractContainer) arg;
         if (!enclosingArg.isObject()) {
             errorProfile.enter();
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.OBJECT_NOT_SPECIFIED);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R
index c2b1e266f5..a17ff95059 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/factor.R
@@ -200,17 +200,17 @@ Ops.factor <- function(e1, e2)
 
 ### NB for next four:
 ### a factor has levels before class in attribute list (PR#6799)
-#`[.factor` <- function(x, ..., drop = FALSE)
-#{
-#    y <- NextMethod("[")
-#    attr(y,"contrasts") <- attr(x,"contrasts")
-#    attr(y,"levels") <- attr(x,"levels")
-#    class(y) <- oldClass(x)
-#    lev <- levels(x)
-#    if (drop)
-#        factor(y, exclude = if(anyNA(levels(x))) NULL else NA ) else y
-#}
-#
+`[.factor` <- function(x, ..., drop = FALSE)
+{
+    y <- NextMethod("[")
+    attr(y,"contrasts") <- attr(x,"contrasts")
+    attr(y,"levels") <- attr(x,"levels")
+    class(y) <- oldClass(x)
+    lev <- levels(x)
+    if (drop)
+        factor(y, exclude = if(anyNA(levels(x))) NULL else NA ) else y
+}
+
 #`[<-.factor` <- function(x, ..., value)
 #{
 #    lx <- levels(x)
@@ -226,15 +226,15 @@ Ops.factor <- function(e1, e2)
 #    x
 #}
 #
-#`[[.factor` <- function(x, ...)
-#{
-#    y <- NextMethod("[[")
-#    attr(y,"contrasts") <- attr(x,"contrasts")
-#    attr(y,"levels") <- attr(x,"levels")
-#    class(y) <- oldClass(x)
-#    y
-#}
-#
+`[[.factor` <- function(x, ...)
+{
+    y <- NextMethod("[[")
+    attr(y,"contrasts") <- attr(x,"contrasts")
+    attr(y,"levels") <- attr(x,"levels")
+    class(y) <- oldClass(x)
+    y
+}
+
 ### added for 2.12.0
 #`[[<-.factor` <- function(x, ..., value)
 #{
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R
index d3557b5963..926707dab2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/R/sort.R
@@ -125,7 +125,9 @@ order <- function(..., na.last = TRUE, decreasing = FALSE)
 sort.list <- function(x, partial = NULL, na.last = TRUE, decreasing = FALSE,
                       method = c("shell", "quick", "radix"))
 {
-    if (missing(method) && is.factor(x) && nlevels(x) < 100000) method <-"radix"
+	# TODO: implement radix sort
+	if (missing(method) && is.factor(x) && nlevels(x) < 100000) method <-"shell"
+#    if (missing(method) && is.factor(x) && nlevels(x) < 100000) method <-"radix"
     method <- match.arg(method)
     if(!is.atomic(x))
         stop("'x' must be atomic for 'sort.list'\nHave you called 'sort' on a list?")
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
index b09d0317eb..bf545cbf47 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
@@ -47,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.RPromise.RPromiseFactory;
  * {@link #matchArguments(VirtualFrame, RFunction, UnmatchedArguments, SourceSection, SourceSection)}
  * . The other match functions are used for special cases, where builtins make it necessary to
  * re-match parameters, e.g.:
- * {@link #matchArgumentsEvaluated(VirtualFrame, RFunction, EvaluatedArguments, SourceSection, PromiseHelperNode)}
+ * {@link #matchArgumentsEvaluated(VirtualFrame, RFunction, EvaluatedArguments, SourceSection, PromiseHelperNode, boolean)}
  * for 'UseMethod' and
  * {@link #matchArgumentsInlined(VirtualFrame, RFunction, UnmatchedArguments, SourceSection, SourceSection)}
  * for builtins which are implemented in Java ( @see {@link RBuiltinNode#inline(InlinedArguments)}
@@ -156,15 +156,17 @@ public class ArgumentMatcher {
      * @param evaluatedArgs The arguments which are already in evaluated form (as they are directly
      *            taken from the stack)
      * @param callSrc The source code of the call
+     * @param forNextMethod matching when evaluating NextMethod
      *
      * @return A Fresh {@link EvaluatedArguments} containing the arguments rearranged and stuffed
      *         with default values (in the form of {@link RPromise}s where needed)
      */
-    public static EvaluatedArguments matchArgumentsEvaluated(VirtualFrame frame, RFunction function, EvaluatedArguments evaluatedArgs, SourceSection callSrc, PromiseHelperNode promiseHelper) {
+    public static EvaluatedArguments matchArgumentsEvaluated(VirtualFrame frame, RFunction function, EvaluatedArguments evaluatedArgs, SourceSection callSrc, PromiseHelperNode promiseHelper,
+                    boolean forNextMethod) {
         RRootNode rootNode = (RRootNode) function.getTarget().getRootNode();
         FormalArguments formals = rootNode.getFormalArguments();
         Object[] evaledArgs = permuteArguments(function, evaluatedArgs.getEvaluatedArgs(), evaluatedArgs.getNames(), formals, new VarArgsAsObjectArrayFactory(), new ObjectArrayFactory(), callSrc,
-                        null);
+                        null, forNextMethod);
 
         // Replace RMissing with default value!
         RNode[] defaultArgs = formals.getDefaultArgs();
@@ -220,7 +222,7 @@ public class ArgumentMatcher {
      * @return A list of {@link RNode}s which consist of the given arguments in the correct order
      *         and wrapped into the proper {@link PromiseNode}s
      * @see #permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory,
-     *      ArrayFactory, SourceSection, SourceSection)
+     *      ArrayFactory, SourceSection, SourceSection, boolean)
      */
     private static RNode[] matchNodes(VirtualFrame frame, RFunction function, RNode[] suppliedArgs, String[] suppliedNames, SourceSection callSrc, SourceSection argsSrc, boolean isForInlinedBuiltin,
                     ClosureCache closureCache) {
@@ -229,7 +231,7 @@ public class ArgumentMatcher {
         FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
 
         // Rearrange arguments
-        RNode[] resultArgs = permuteArguments(function, suppliedArgs, suppliedNames, formals, new VarArgsAsObjectArrayNodeFactory(), new RNodeArrayFactory(), callSrc, argsSrc);
+        RNode[] resultArgs = permuteArguments(function, suppliedArgs, suppliedNames, formals, new VarArgsAsObjectArrayNodeFactory(), new RNodeArrayFactory(), callSrc, argsSrc, false);
 
         PromiseWrapper wrapper = isForInlinedBuiltin ? new BuiltinInitPromiseWrapper() : new DefaultPromiseWrapper();
         return wrapInPromises(function, resultArgs, formals, wrapper, closureCache, callSrc);
@@ -247,13 +249,14 @@ public class ArgumentMatcher {
      * @param arrFactory An abstraction for the generic creation of type safe arrays
      * @param callSrc The source of the function call currently executed
      * @param argsSrc The source code encapsulating the arguments, for debugging purposes
+     * @param forNextMethod matching when evaluating NextMethod
      *
      * @param <T> The type of the given arguments
      * @return An array of type <T> with the supplied arguments in the correct order
      */
     @TruffleBoundary
     private static <T> T[] permuteArguments(RFunction function, T[] suppliedArgs, String[] suppliedNames, FormalArguments formals, VarArgsFactory<T> listFactory, ArrayFactory<T> arrFactory,
-                    SourceSection callSrc, SourceSection argsSrc) {
+                    SourceSection callSrc, SourceSection argsSrc, boolean forNextMethod) {
         String[] formalNames = formals.getNames();
 
         // Preparations
@@ -272,7 +275,7 @@ public class ArgumentMatcher {
             }
 
             // Search for argument name inside formal arguments
-            int fi = findParameterPosition(formalNames, suppliedNames[si], matchedFormalArgs, si, hasVarArgs, suppliedArgs[si], callSrc, argsSrc, varArgIndex);
+            int fi = findParameterPosition(formalNames, suppliedNames[si], matchedFormalArgs, si, hasVarArgs, suppliedArgs[si], callSrc, argsSrc, varArgIndex, forNextMethod);
             if (fi >= 0) {
                 resultArgs[fi] = suppliedArgs[si];
                 matchedSuppliedArgs.set(si);
@@ -289,8 +292,11 @@ public class ArgumentMatcher {
         for (int fi = 0; fi < resultArgs.length; fi++) {
             // Unmatched?
             if (!matchedFormalArgs.get(fi)) {
-                while (siCursor.hasNext() && siCursor.nextIndex() < suppliedNames.length && suppliedNames[siCursor.nextIndex()] != null && !suppliedNames[siCursor.nextIndex()].isEmpty()) {
+                while (siCursor.hasNext() && siCursor.nextIndex() < suppliedNames.length && suppliedNames[siCursor.nextIndex()] != null && !suppliedNames[siCursor.nextIndex()].isEmpty() &&
+                                !forNextMethod) {
                     // Slide over named parameters and find subsequent location of unnamed parameter
+                    // (if processing args for NextMethod, try to match yet unmatched named
+                    // parameters - do not slide over them)
                     siCursor.next();
                 }
                 boolean followsDots = hasVarArgs && fi >= varArgIndex;
@@ -374,7 +380,7 @@ public class ArgumentMatcher {
 
     /**
      * Used in
-     * {@link ArgumentMatcher#permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory, ArrayFactory, SourceSection, SourceSection)}
+     * {@link ArgumentMatcher#permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory, ArrayFactory, SourceSection, SourceSection, boolean)}
      * for iteration over suppliedArgs.
      *
      * @param <T>
@@ -461,12 +467,13 @@ public class ArgumentMatcher {
      * @param callSrc
      * @param argsSrc
      * @param varArgIndex
+     * @param forNextMethod
      *
      * @return The position of the given suppliedName inside the formalNames. Throws errors if the
      *         argument has been matched before
      */
     private static <T> int findParameterPosition(String[] formalNames, String suppliedName, BitSet matchedSuppliedArgs, int suppliedIndex, boolean hasVarArgs, T debugArgNode, SourceSection callSrc,
-                    SourceSection argsSrc, int varArgIndex) {
+                    SourceSection argsSrc, int varArgIndex, boolean forNextMethod) {
         int found = -1;
         for (int i = 0; i < formalNames.length; i++) {
             if (formalNames[i] == null) {
@@ -494,7 +501,7 @@ public class ArgumentMatcher {
                 matchedSuppliedArgs.set(found);
             }
         }
-        if (found >= 0 || hasVarArgs) {
+        if (found >= 0 || hasVarArgs || forNextMethod) {
             return found;
         }
         // Error!
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
index 6d0b3f9b7a..400b6c1d7b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
@@ -4,7 +4,7 @@
  * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, Oracle and/or its affiliates
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -28,7 +28,11 @@ public abstract class DispatchedCallNode extends RNode {
     }
 
     public static DispatchedCallNode create(final String genericName, final String dispatchType, final Object[] args) {
-        return new UninitializedDispatchedCallNode(genericName, dispatchType, args);
+        return new UninitializedDispatchedCallNode(genericName, null, dispatchType, args);
+    }
+
+    public static DispatchedCallNode create(final String genericName, final String enclosingName, final String dispatchType, final Object[] args) {
+        return new UninitializedDispatchedCallNode(genericName, enclosingName, dispatchType, args);
     }
 
     @Override
@@ -49,23 +53,26 @@ public abstract class DispatchedCallNode extends RNode {
     private static final class UninitializedDispatchedCallNode extends DispatchedCallNode {
         private final int depth;
         private final String genericName;
+        private final String enclosingName;
         private final String dispatchType;
         @CompilationFinal private final Object[] args;
 
-        public UninitializedDispatchedCallNode(final String genericName, final String dispatchType, Object[] args) {
+        public UninitializedDispatchedCallNode(final String genericName, final String enclosingName, final String dispatchType, Object[] args) {
             this.genericName = genericName;
+            this.enclosingName = enclosingName;
             this.depth = 0;
             this.dispatchType = dispatchType;
             this.args = args;
         }
 
         public UninitializedDispatchedCallNode(final String genericName, final String dispatchType) {
-            this(genericName, dispatchType, null);
+            this(genericName, dispatchType, null, null);
         }
 
         private UninitializedDispatchedCallNode(final UninitializedDispatchedCallNode copy, final int depth) {
             this.depth = depth;
             this.genericName = copy.genericName;
+            this.enclosingName = copy.enclosingName;
             this.dispatchType = copy.dispatchType;
             this.args = null;
         }
@@ -98,7 +105,7 @@ public abstract class DispatchedCallNode extends RNode {
                 return new UseMethodDispatchNode(this.genericName, type);
             }
             if (this.dispatchType == RRuntime.NEXT_METHOD) {
-                return new NextMethodDispatchNode(this.genericName, type, this.args);
+                return new NextMethodDispatchNode(this.genericName, type, this.args, enclosingName);
             }
             throw RInternalError.shouldNotReachHere();
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
index 728650323c..5a23fca15b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
@@ -350,14 +350,14 @@ class GroupDispatchNode extends S3DispatchNode {
 
     protected Object callBuiltin(VirtualFrame frame, final Object[] evaluatedArgs, final String[] argNames) {
         initBuiltin(frame);
-        EvaluatedArguments reorderedArgs = reorderArgs(frame, builtinFunc, evaluatedArgs, argNames, this.hasVararg, this.callSrc);
+        EvaluatedArguments reorderedArgs = reorderArgs(frame, builtinFunc, evaluatedArgs, argNames, this.hasVararg, this.callSrc, false);
         Object[] argObject = RArguments.create(builtinFunc, this.callSrc, RArguments.getDepth(frame), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
         indirectCallNode.assignSourceSection(this.callSrc);
         return indirectCallNode.call(frame, builtinFunc.getTarget(), argObject);
     }
 
     protected Object executeHelper(VirtualFrame frame, final Object[] evaluatedArgs, final String[] argNames) {
-        EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argNames, this.hasVararg, this.callSrc);
+        EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argNames, this.hasVararg, this.callSrc, false);
         Object[] argObject = RArguments.createS3Args(targetFunction, this.callSrc, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
         // todo: cannot create frame descriptors in compiled code
         FrameDescriptor s3VarDefFrameDescriptor = new FrameDescriptor();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
index 369fff8a0f..3035bc15ca 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
@@ -39,10 +39,11 @@ public class NextMethodDispatchNode extends S3DispatchNode {
     private boolean lastHasGroup;
     @CompilationFinal private final Object[] args;
 
-    NextMethodDispatchNode(String genericName, RStringVector type, Object[] args) {
+    NextMethodDispatchNode(String genericName, RStringVector type, Object[] args, String storedFunctionName) {
         this.genericName = genericName;
         this.type = type;
         this.args = args;
+        this.storedFunctionName = storedFunctionName;
     }
 
     @Override
@@ -63,13 +64,53 @@ public class NextMethodDispatchNode extends S3DispatchNode {
         return executeHelper(frame);
     }
 
+    private EvaluatedArguments processArgs(VirtualFrame frame) {
+        int argsLength = args == null ? 0 : args.length;
+        // Extract arguments from current frame...
+        int funArgsLength = RArguments.getArgumentsLength(frame);
+        assert RArguments.getNamesLength(frame) == 0 || RArguments.getNamesLength(frame) == funArgsLength;
+        boolean hasNames = RArguments.getNamesLength(frame) > 0;
+        Object[] argValues = new Object[funArgsLength + argsLength];
+        String[] argNames = hasNames ? new String[funArgsLength + argsLength] : null;
+        int index = 0;
+        for (int fi = 0; fi < funArgsLength; fi++) {
+            Object argVal = RArguments.getArgument(frame, fi);
+            if (argVal instanceof RArgsValuesAndNames) {
+                RArgsValuesAndNames varArgs = (RArgsValuesAndNames) argVal;
+                int varArgsLength = varArgs.length();
+                if (varArgsLength > 1) {
+                    argValues = Utils.resizeArray(argValues, argValues.length + varArgsLength - 1);
+                }
+                System.arraycopy(varArgs.getValues(), 0, argValues, index, varArgsLength);
+                if (hasNames) {
+                    if (varArgsLength > 1) {
+                        argNames = Utils.resizeArray(argNames, argNames.length + varArgsLength - 1);
+                    }
+                    if (varArgs.getNames() != null) {
+                        System.arraycopy(varArgs.getNames(), 0, argNames, index, varArgsLength);
+                    }
+                } else if (varArgs.getNames() != null) {
+                    argNames = new String[funArgsLength + argsLength];
+                    System.arraycopy(varArgs.getNames(), 0, argNames, index, varArgsLength);
+                }
+                index += varArgsLength;
+            } else {
+                argValues[index] = argVal;
+                if (hasNames) {
+                    argNames[index] = RArguments.getName(frame, fi);
+                }
+                index++;
+            }
+        }
+        if (argsLength > 0) {
+            System.arraycopy(args, 0, argValues, RArguments.getArgumentsLength(frame), args.length);
+        }
+        return reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection(), true);
+    }
+
     private Object executeHelper(VirtualFrame frame) {
-        // Merge arguments passed to current function with arguments passed to NextMethod call.
-        final Object[] mergedArgs = new Object[RArguments.getArgumentsLength(frame) + args.length];
-        RArguments.copyArgumentsInto(frame, mergedArgs);
-        System.arraycopy(args, 0, mergedArgs, RArguments.getArgumentsLength(frame), args.length);
-        // TODO: implement names passing
-        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), RArguments.getDepth(frame) + 1, mergedArgs, RArguments.EMPTY_STRING_ARRAY);
+        EvaluatedArguments evaledArgs = processArgs(frame);
+        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), RArguments.getDepth(frame) + 1, evaledArgs.getEvaluatedArgs(), evaledArgs.getNames());
         // todo: cannot create frame descriptors in compiled code
         FrameDescriptor frameDescriptor = new FrameDescriptor();
         FrameSlotChangeMonitor.initializeFrameDescriptor(frameDescriptor, true);
@@ -145,7 +186,9 @@ public class NextMethodDispatchNode extends S3DispatchNode {
 
     private void readGenericVars(VirtualFrame frame) {
         genDefEnv = RArguments.getS3DefEnv(frame);
-        // TODO if(genDefEnv == null) genDefEnv = globalenv
+        if (genDefEnv == null) {
+            genDefEnv = RArguments.getEnclosingFrame(frame);
+        }
         genCallEnv = RArguments.getS3CallEnv(frame);
         if (genCallEnv == null) {
             genCallEnv = frame.materialize();
@@ -156,7 +199,10 @@ public class NextMethodDispatchNode extends S3DispatchNode {
         } else {
             handlePresentGroup();
         }
-        storedFunctionName = RArguments.getS3Method(frame);
+        String functionName = RArguments.getS3Method(frame);
+        if (functionName != null) {
+            storedFunctionName = functionName;
+        }
     }
 
     private void handleMissingGroup() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
index 11cc9d291f..3b3b9a05c5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
@@ -46,7 +46,8 @@ public abstract class S3DispatchNode extends DispatchNode {
     // TODO: the executeHelper methods share quite a bit of code, but is it better or worse from
     // having one method with a rather convoluted control flow structure?
 
-    protected EvaluatedArguments reorderArgs(VirtualFrame frame, RFunction func, Object[] evaluatedArgs, final String[] argNames, final boolean hasVarArgs, final SourceSection callSrc) {
+    protected EvaluatedArguments reorderArgs(VirtualFrame frame, RFunction func, Object[] evaluatedArgs, final String[] argNames, final boolean hasVarArgs, final SourceSection callSrc,
+                    boolean forNextMethod) {
         String[] evaluatedArgNames = null;
         Object[] evaluatedArgsValues = evaluatedArgs;
         int argCount = evaluatedArgs.length;
@@ -85,7 +86,7 @@ public abstract class S3DispatchNode extends DispatchNode {
         }
         EvaluatedArguments evaledArgs = EvaluatedArguments.create(evaluatedArgsValues, evaluatedArgNames);
         // ...to match them against the chosen function's formal arguments
-        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, callSrc, promiseHelper);
+        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, callSrc, promiseHelper, forNextMethod);
         return reorderedArgs;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
index 6215fd0655..72b10dd14d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
@@ -86,7 +86,7 @@ public class UseMethodDispatchNode extends S3DispatchNode {
                 argNames[fi] = RArguments.getName(frame, fi);
             }
         }
-        EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection());
+        EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection(), false);
         return executeHelper2(callerFrame, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
     }
 
@@ -115,7 +115,7 @@ public class UseMethodDispatchNode extends S3DispatchNode {
         // ...and use them as 'supplied' arguments...
         EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, null);
         // ...to match them against the chosen function's formal arguments
-        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(callerFrame, targetFunction, evaledArgs, getEncapsulatingSourceSection(), promiseHelper);
+        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(callerFrame, targetFunction, evaledArgs, getEncapsulatingSourceSection(), promiseHelper, false);
         return executeHelper2(callerFrame, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java
index ef73dffc18..194ab5c5e8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToVectorNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -62,4 +62,10 @@ public abstract class CastToVectorNode extends CastNode {
     protected RAbstractVector cast(RAbstractVector vector) {
         return vector;
     }
+
+    @Specialization
+    protected RAbstractVector cast(RFactor factor) {
+        return factor.getVector();
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
index 6c48875aee..a9d49a7e57 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
@@ -213,6 +213,12 @@ public final class RArguments {
         return a;
     }
 
+    public static boolean hasS3Args(Frame frame) {
+        Object[] args = getArgumentsWithEvalCheck(frame);
+        int s3StartIndex = getS3StartIndex(args);
+        return args.length > s3StartIndex;
+    }
+
     public static String getS3Generic(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
@@ -230,8 +236,11 @@ public final class RArguments {
     public static RStringVector getS3Class(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        return (RStringVector) args[s3StartIndex + S3_INDEX_CLASS];
+        if (args.length <= s3StartIndex) {
+            return null;
+        } else {
+            return (RStringVector) args[s3StartIndex + S3_INDEX_CLASS];
+        }
     }
 
     public static void setS3Class(Frame frame, final RStringVector klass) {
@@ -244,8 +253,11 @@ public final class RArguments {
     public static String getS3Method(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        return (String) args[s3StartIndex + S3_INDEX_METHOD];
+        if (args.length <= s3StartIndex) {
+            return null;
+        } else {
+            return (String) args[s3StartIndex + S3_INDEX_METHOD];
+        }
     }
 
     public static void setS3Method(Frame frame, final String method) {
@@ -258,8 +270,11 @@ public final class RArguments {
     public static Frame getS3DefEnv(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        return (Frame) args[s3StartIndex + S3_INDEX_DEF_ENV];
+        if (args.length <= s3StartIndex) {
+            return null;
+        } else {
+            return (Frame) args[s3StartIndex + S3_INDEX_DEF_ENV];
+        }
     }
 
     public static void setS3DefEnv(Frame frame, Frame defEnv) {
@@ -272,8 +287,11 @@ public final class RArguments {
     public static Frame getS3CallEnv(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        return (Frame) args[s3StartIndex + S3_INDEX_CALL_ENV];
+        if (args.length <= s3StartIndex) {
+            return null;
+        } else {
+            return (Frame) args[s3StartIndex + S3_INDEX_CALL_ENV];
+        }
     }
 
     public static void setS3CallEnv(Frame frame, Frame callEnv) {
@@ -286,8 +304,11 @@ public final class RArguments {
     public static String getS3Group(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        return (String) args[s3StartIndex + S3_INDEX_GROUP];
+        if (args.length <= s3StartIndex) {
+            return null;
+        } else {
+            return (String) args[s3StartIndex + S3_INDEX_GROUP];
+        }
     }
 
     public static void setS3Group(Frame frame, final String group) {
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 922317c582..dad12f6b3b 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
@@ -16699,6 +16699,11 @@ Error in typeof(...) : unused arguments (2, 3, 4)
 #{ x<-factor(c("a", "b", "a")); typeof(x) }
 [1] "integer"
 
+##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUnique
+#{x<-factor(c("a", "b", "a")); unique(x) }
+[1] a b
+Levels: a b
+
 ##com.oracle.truffle.r.test.simple.TestSimpleBuiltins.testUnlist
 #{ names(unlist(list(a=list(b=list("1"))))) }
 [1] "a.b"
@@ -58178,6 +58183,40 @@ Error in x[[NA]] <- c(7, 42) : attempt to select more than one element
 #{ x<-list(1,2,3,4); dim(x)<-c(2,2); x[[NA]]<-c(7, 42, 1); x }
 Error in x[[NA]] <- c(7, 42, 1) : attempt to select more than one element
 
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", "b", "a")); `[.factor`(x, 1) }
+[1] a
+Levels: a b
+
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", "b", "a")); `[.factor`(x, 1, drop=FALSE) }
+[1] a
+Levels: a b
+
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", "b", "a")); `[.factor`(x, 1, drop=TRUE) }
+[1] a
+Levels: a
+
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", "b", "a")); `[[.factor`(x, 1) }
+[1] a
+Levels: a b
+
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", z="b", "a")); `[[.factor`(x, "z") }
+[1] b
+Levels: a b
+
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", zz="b", "a")); `[[.factor`(x, "z", exact=FALSE) }
+[1] b
+Levels: a b
+
+##com.oracle.truffle.r.test.simple.TestSimpleVectors.testObjectDirectAccess
+#{ x<-factor(c("a", zz="b", "a")); `[[.factor`(x, "z", exact=TRUE) }
+Error in `[[.default`(x, "z", exact = TRUE) : subscript out of bounds
+
 ##com.oracle.truffle.r.test.simple.TestSimpleVectors.testPrint
 #{ mp<-getOption("max.print"); options(max.print=3); x<-c(1,2,3,4,5); attr(x, "foo")<-"foo"; print(x); options(max.print=mp) }
 [1] 1 2 3
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 75005563a3..a28e3163a9 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
@@ -16464,6 +16464,11 @@ public class AllTests extends TestBase {
         assertEval("{  typeof(seq(1,2)) }");
     }
 
+    @Test
+    public void TestSimpleBuiltins_testUnique_525d049c6d816b0aa8f72e1deeb58241() {
+        assertEval("{x<-factor(c(\"a\", \"b\", \"a\")); unique(x) }");
+    }
+
     @Test
     public void TestSimpleBuiltins_testUnlist_52964c4cb43a47670c1f4d283abd1e1d() {
         assertEval("{ unlist(list(\"hello\", \"hi\")) }");
@@ -24984,6 +24989,41 @@ public class AllTests extends TestBase {
         assertEvalError("{ x<-list(1,2,3,4); dim(x)<-c(2,2); x[[NA, 1]]<-c(7, 42, 1); x }");
     }
 
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_01eabfb2c4e15bb5a7006319a4337fd2() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1) }");
+    }
+
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_6f3b4849970406b2315eabe72503d201() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=TRUE) }");
+    }
+
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_1be9c6acb933e025c3d74895ff9d7d02() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=FALSE) }");
+    }
+
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_b79b4f8ad5b7cc5a833d720266dd1a1d() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[[.factor`(x, 1) }");
+    }
+
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_660880a286cd3118a6c3ffb5f976c827() {
+        assertEval("{ x<-factor(c(\"a\", z=\"b\", \"a\")); `[[.factor`(x, \"z\") }");
+    }
+
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_c1ed9317efa96b573485632db9fb0241() {
+        assertEval("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=FALSE) }");
+    }
+
+    @Test
+    public void TestSimpleVectors_testObjectDirectAccess_68b89b4e74c36289fd7aef12dbd3c682() {
+        assertEvalError("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=TRUE) }");
+    }
+
     @Test
     public void TestSimpleVectors_testPrint_ac880bff260f234821af9ee036453e82() {
         assertEval("{ x<-1:8; dim(x)<-c(2, 4); x }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java
index e66c5dd561..51c5c1358e 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/TestSimpleVectors.java
@@ -16,6 +16,18 @@ import com.oracle.truffle.r.test.*;
 
 public class TestSimpleVectors extends TestBase {
 
+    @Test
+    public void testObjectDirectAccess() {
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1) }");
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=TRUE) }");
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[.factor`(x, 1, drop=FALSE) }");
+
+        assertEval("{ x<-factor(c(\"a\", \"b\", \"a\")); `[[.factor`(x, 1) }");
+        assertEval("{ x<-factor(c(\"a\", z=\"b\", \"a\")); `[[.factor`(x, \"z\") }");
+        assertEvalError("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=TRUE) }");
+        assertEval("{ x<-factor(c(\"a\", zz=\"b\", \"a\")); `[[.factor`(x, \"z\", exact=FALSE) }");
+    }
+
     @Test
     public void testFunctionAccess() {
         assertEval("{ x<-matrix(1:4, ncol=2); y<-`[`(x); y }");
-- 
GitLab