diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java
index cdd90a090899456d7e339fbb92da3e3f5d0f06a8..6194da9a6cd385a21cac054592523df563b7df4f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java
@@ -106,9 +106,9 @@ public abstract class StandardGeneric extends RBuiltinNode.Arg2 {
         }
         if (collectArgumentsNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            collectArgumentsNode = insert(CollectGenericArgumentsNodeGen.create(sigArgs.getDataWithoutCopying(), sigLength));
+            collectArgumentsNode = insert(CollectGenericArgumentsNodeGen.create(sigLength));
         }
-        RStringVector classes = collectArgumentsNode.execute(frame, sigArgs, sigLength);
+        RStringVector classes = collectArgumentsNode.execute(frame, sigLength);
         Object ret = dispatchGeneric.executeObject(frame, mtable, classes, def, fname);
         return ret;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
index c5fe1e156efc33ac413d46e50f47f837a7cc0f4c..1eb635ee7b513906f05845454a3eccf69fbe6f2d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
@@ -22,107 +22,119 @@
  */
 package com.oracle.truffle.r.nodes.objects;
 
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.FrameDescriptor;
-import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.SlowPathException;
 import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyScalarNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyScalarNodeGen;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
-import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
-import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
-// transcribed from /src/library/methods/src/methods_list_dispatch.c (R_dispatch_generic function)
+// transcribed from /src/library/methods/src/methods_list_dispatch.c (R_dispatchGeneric function)
 
 /*
  * Used to collect arguments of the generic function for S4 method dispatch. Modeled after {@link CollectArgumentsNode}.
+ * The way GnuR determines the classes of the arguments is by looking up the names of the formal arguments in the dispatching function.
+ * However, the dispatching function may define default values for arguments that can change the signature of the actual arguments.
+ * The function lookup must be done by using the original actual arguments (i.e. actual arguments without default values).
+ * Since the arguments have already been matched and are ordered, we can just look at the arguments in the frame.
+ * Varargs do not appear in the formal signature, therefore any vararg parameter must be skipped.
  */
 public abstract class CollectGenericArgumentsNode extends RBaseNode {
 
     // TODO: re-do with a multi-element cache? (list comparison will have some cost, though)
 
-    @Children private final LocalReadVariableNode[] argReads;
     @Children private final ClassHierarchyScalarNode[] classHierarchyNodes;
     @Child private ClassHierarchyScalarNode classHierarchyNodeSlowPath;
     @Child private PromiseCheckHelperNode promiseHelper = new PromiseCheckHelperNode();
 
+    private final int nProvidedArgs;
+
     private final ConditionProfile valueMissingProfile = ConditionProfile.createBinaryProfile();
 
-    public abstract RStringVector execute(VirtualFrame frame, RList arguments, int argLength);
+    public abstract RStringVector execute(VirtualFrame frame, int argLength);
 
-    protected CollectGenericArgumentsNode(Object[] arguments, int argLength) {
-        LocalReadVariableNode[] reads = new LocalReadVariableNode[argLength];
+    protected CollectGenericArgumentsNode(int argLength) {
         ClassHierarchyScalarNode[] hierarchyNodes = new ClassHierarchyScalarNode[argLength];
         for (int i = 0; i < argLength; i++) {
-            RSymbol s = (RSymbol) arguments[i];
-            reads[i] = LocalReadVariableNode.create(s.getName(), true);
             hierarchyNodes[i] = ClassHierarchyScalarNodeGen.create();
         }
-        argReads = insert(reads);
-        classHierarchyNodes = insert(hierarchyNodes);
+        nProvidedArgs = argLength;
+        classHierarchyNodes = hierarchyNodes;
     }
 
     @ExplodeLoop
     @Specialization(rewriteOn = SlowPathException.class)
-    protected RStringVector combineCached(VirtualFrame frame, RList arguments, int argLength) throws SlowPathException {
-        if (argLength != argReads.length) {
+    protected RStringVector combineCached(VirtualFrame frame, int argLength) throws SlowPathException {
+        int nActualArgs = RArguments.getArgumentsLength(frame);
+        if (argLength != nProvidedArgs || !(nActualArgs == nProvidedArgs || nActualArgs == nProvidedArgs + 1)) {
             throw new SlowPathException();
         }
-        String[] result = new String[argReads.length];
-        for (int i = 0; i < argReads.length; i++) {
-            Object cachedId = argReads[i].getIdentifier();
-            String id = ((RSymbol) (arguments.getDataAt(i))).getName();
-            assert cachedId instanceof String && Utils.isInterned((String) cachedId) && Utils.isInterned(id);
-            if (cachedId != id) {
-                throw new SlowPathException();
+        String[] result = new String[nProvidedArgs];
+
+        // The length of the actual and formal arguments may not be equal because "..." is just
+        // ignored in formals (i.e. '.SigArgs').
+        assert nActualArgs == result.length || nActualArgs == result.length + 1;
+
+        // Intentionally using 'i' as loop variable since nActualArgs >=
+        // signatureArgumentNames.length
+        int j = 0;
+        for (int i = 0; i < nProvidedArgs; i++) {
+            Object value = RArguments.getArgument(frame, j);
+            if (value instanceof RArgsValuesAndNames) {
+                j++;
+                value = RArguments.getArgument(frame, j);
             }
-            Object value = argReads[i].execute(frame);
             if (value == REmpty.instance || value == RMissing.instance) {
                 value = null;
             }
-            result[i] = valueMissingProfile.profile(value == null) ? "missing" : classHierarchyNodes[i].executeString(promiseHelper.checkEvaluate(frame, value));
+            Object evaledArg = promiseHelper.checkEvaluate(frame, value);
+            assert !(evaledArg instanceof RArgsValuesAndNames);
+            result[i] = valueMissingProfile.profile(value == null) ? "missing" : classHierarchyNodes[i].executeString(evaledArg);
+            j++;
         }
         return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR);
     }
 
     @Specialization
-    protected RStringVector combine(VirtualFrame frame, RList arguments, int argLength) {
-        return readFromMaterialized(frame.materialize(), arguments, argLength);
+    protected RStringVector combine(VirtualFrame frame, int argLength) {
+        return readFromMaterialized(frame.materialize(), argLength);
     }
 
-    @TruffleBoundary
-    private RStringVector readFromMaterialized(MaterializedFrame frame, RList arguments, int argLength) {
-        CompilerAsserts.neverPartOfCompilation();
-        classHierarchyNodeSlowPath = insert(ClassHierarchyScalarNodeGen.create());
+    private RStringVector readFromMaterialized(MaterializedFrame frame, int argLength) {
+        if (classHierarchyNodeSlowPath == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            classHierarchyNodeSlowPath = insert(ClassHierarchyScalarNodeGen.create());
+        }
+
+        int nActualArgs = RArguments.getArgumentsLength(frame);
+        assert nActualArgs >= argLength;
+
         String[] result = new String[argLength];
-        FrameDescriptor desc = frame.getFrameDescriptor();
-        for (int i = 0; i < argLength; i++) {
-            RSymbol s = (RSymbol) arguments.getDataAt(i);
-            FrameSlot slot = desc.findFrameSlot(s.getName());
-            if (slot == null) {
-                result[i] = "missing";
-            } else {
-                Object value = FrameSlotChangeMonitor.getValue(slot, frame);
+        for (int j = 0, i = 0; i < argLength && j < nActualArgs; j++) {
+            Object value = RArguments.getArgument(frame, j);
+            if (value == REmpty.instance || value == RMissing.instance) {
+                value = null;
+            }
+            if (!(value instanceof RArgsValuesAndNames)) {
                 if (value instanceof RPromise) {
                     value = PromiseHelperNode.evaluateSlowPath((RPromise) value);
                 }
-                result[i] = classHierarchyNodeSlowPath.executeString(value);
+                assert !(value instanceof RArgsValuesAndNames);
+                result[i] = value == null ? "missing" : classHierarchyNodeSlowPath.executeString(value);
+                i++;
             }
         }
         return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java
index c7403d171a61c224ed320599ac40e8a8cbe4b18b..baf6272c4fe748045beb93bf5c76293563c11db4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java
@@ -19,6 +19,7 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.function.CallMatcherNode;
 import com.oracle.truffle.r.nodes.function.signature.CollectArgumentsNode;
 import com.oracle.truffle.r.nodes.function.signature.CollectArgumentsNodeGen;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S4Args;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -41,13 +42,18 @@ final class ExecuteMethod extends RBaseNode {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             collectArgs = insert(CollectArgumentsNodeGen.create());
         }
+        ArgumentsSignature signature = RArguments.getSignature(frame);
+
+        // Collect arguments; we cannot use the arguments of the original call because there might
+        // be overriding default arguments.
+        Object[] cumulativeArgs = collectArgs.execute(frame, signature);
+
         if (callMatcher == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             callMatcher = insert(CallMatcherNode.create(false));
         }
 
         S4Args s4Args = new S4Args(readDefined.execute(frame), readMethod.execute(frame), readTarget.execute(frame), readGeneric.execute(frame), readMethods.execute(frame));
-
-        return callMatcher.execute(frame, RArguments.getSignature(frame), RArguments.getArguments(frame), fdef, fname, s4Args);
+        return callMatcher.execute(frame, signature, cumulativeArgs, fdef, fname, s4Args);
     }
 }
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 05bc42255cc726b50f0a2a4ac27d88f59eb5f37e..3a2a1dc162c5e7f6c790858209f562616f1f2f4e 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
@@ -310,8 +310,14 @@ Creating a generic function from function ‘foo.bar’ in the global environmen
 [1] "primitive, A, B"
 [1] "primitive, B, A"
 
+##com.oracle.truffle.r.test.S4.TestS4.runRSourceTests#
+#{ source("tmptest/S4/dispatchDefaultArgValues.R") }
+subsetFooMissingDrop[2,3,drop=missing]
+subsetFoo[2,3,drop=TRUE]
+
 ##com.oracle.truffle.r.test.S4.TestS4.runRSourceTests#
 #{ source("tmptest/S4/groupGenericS4Dispatch.R") }
+[1] "s4 dispatched"
 
 ##com.oracle.truffle.r.test.S4.TestS4.runRSourceTests#Output.IgnoreErrorContext#
 #{ source("tmptest/S4/refClasses0.R") }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/dispatchDefaultArgValues.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/dispatchDefaultArgValues.R
new file mode 100644
index 0000000000000000000000000000000000000000..2a549a10be102eefafe6992869f20134cdec206c
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/dispatchDefaultArgValues.R
@@ -0,0 +1,19 @@
+setClassUnion("DDAVindex", members =  c("numeric", "logical", "character"))
+setClass("DDAVFoo", representation(a = "numeric", b = "numeric"))
+
+subsetFoo <- function(x, i, j, drop) {
+  cat(paste0("subsetFoo[",i,",",j,",drop=",drop,"]\n"))
+  c(x@a[[i]], x@b[[j]])
+}
+
+subsetFooMissingDrop <- function(x, i, j, drop) {
+  cat(paste0("subsetFooMissingDrop[",i,",",j,",drop=missing]\n"))
+  c(x@a[[i]], x@b[[j]])  
+}
+setMethod("[", signature(x = "DDAVFoo", i = "DDAVindex", j = "DDAVindex", drop = "logical"), subsetFoo)
+setMethod("[", signature(x = "DDAVFoo", i = "DDAVindex", j = "DDAVindex", drop = "missing"), subsetFooMissingDrop)
+
+obj <- new("DDAVFoo", a=c(1,2,3),b=c(4,5,6))
+obj[2,3]
+
+obj[drop=T,j=3,i=2]
\ No newline at end of file
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R
index 4d972b633c32067a7fb9261095a309f68036c114..187101d1930fc2786d6cfc6e0ca1c9c2c947c9e0 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/R/groupGenericS4Dispatch.R
@@ -4,4 +4,5 @@ setMethod("%*%", signature(x = "ANY", y = "Foo1234"), function(x, y) { "s4 dispa
 obj <- new("Foo1234")
 x <- matrix(1.1:16.1, 4, 4)
 obj@a <- runif(10)
-x %*% obj
+res <- x %*% obj
+print(res)
\ No newline at end of file