diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
index 9d5c13dcfe9ae8913ad999c075e9bf081fffe569..79c34b0c8f2ce944cc9fe242ea1a2cbcbcb23d96 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
@@ -160,6 +160,6 @@ public abstract class AsCharacter extends RBuiltinNode {
 
     @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
     protected boolean isObject(VirtualFrame frame, RAbstractContainer container) {
-        return container.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+        return container.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
index a458b80f48f29bcfc587993bd249bc27d263aaa9..b92374dc390a6525edd54c1681528003c14468a4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
@@ -114,7 +114,7 @@ public abstract class Dim extends RBuiltinNode {
 
     @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
     protected boolean isObject(VirtualFrame frame, RAbstractContainer container) {
-        return container.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+        return container.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
index a4880dd23caed4b5f7830564447233dd8b159214..01f401ae0a6da28fb3b8bb6a6d2b44b3f0e74519 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
@@ -84,7 +84,7 @@ public abstract class DimNames extends RBuiltinNode {
 
     @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
     protected boolean isObject(VirtualFrame frame, RAbstractContainer container) {
-        return container.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+        return container.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
     }
 
 }
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 27469b2dfe14f63479e62013406eb7a8b07569ae..fadcd66721843b76f43c9bcaa4055dff0337a7bf 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
@@ -290,7 +290,7 @@ public class InfixEmulationFunctions {
 
         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
         protected boolean isObject(VirtualFrame frame, RAbstractContainer x) {
-            return x.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+            return x.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
         }
 
     }
@@ -381,7 +381,7 @@ public class InfixEmulationFunctions {
 
         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
         protected boolean isObject(VirtualFrame frame, RAbstractContainer x) {
-            return x.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+            return x.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
         }
 
     }
@@ -483,7 +483,7 @@ public class InfixEmulationFunctions {
 
         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
         protected boolean isObject(VirtualFrame frame, RAbstractContainer x) {
-            return x.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+            return x.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
         }
     }
 
@@ -517,7 +517,7 @@ public class InfixEmulationFunctions {
 
         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "generic name is interned in the interpreted code for faster comparison")
         protected boolean isObject(VirtualFrame frame, RAbstractContainer x) {
-            return x.isObject(attrProfiles) && !(RArguments.hasS3Args(frame) && RArguments.getS3Generic(frame) == NAME);
+            return x.isObject(attrProfiles) && !(RArguments.getS3Args(frame) != null && RArguments.getS3Args(frame).generic == NAME);
         }
 
     }
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 2954ca86e56f1fdc187c77343e0f42c910572bb4..44251f926f0d64aeab43723a3b296c2f42b4a9e2 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 ReadVariableNode rvnClass;
     @Child private ReadVariableNode rvnGeneric;
     @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode();
+
     @CompilationFinal private String cachedGeneric;
 
     private final BranchProfile errorProfile = BranchProfile.create();
@@ -52,7 +53,7 @@ public abstract class NextMethod extends RBuiltinNode {
             cachedGeneric = generic;
             RFunction enclosingFunction = RArguments.getFunction(frame);
             String enclosingFunctionName = null;
-            if (!RArguments.hasS3Args(frame)) {
+            if (RArguments.getS3Args(frame) == null) {
                 enclosingFunctionName = enclosingFunction.getRootNode().toString();
             }
             DispatchedCallNode newDispatched = DispatchedCallNode.create(generic.intern(), enclosingFunctionName, DispatchType.NextMethod, args, signature);
@@ -73,7 +74,7 @@ public abstract class NextMethod extends RBuiltinNode {
     @Specialization
     protected Object nextMethod(VirtualFrame frame, @SuppressWarnings("unused") RNull generic, Object obj, RArgsValuesAndNames args) {
         controlVisibility();
-        String genericName = RArguments.getS3Generic(frame);
+        String genericName = RArguments.getS3Args(frame) == null ? null : RArguments.getS3Args(frame).generic;
         if (genericName == null || genericName.isEmpty()) {
             errorProfile.enter();
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.GEN_FUNCTION_NOT_SPECIFIED);
@@ -106,7 +107,7 @@ public abstract class NextMethod extends RBuiltinNode {
     }
 
     private RStringVector readType(VirtualFrame frame) {
-        final RStringVector storedClass = RArguments.getS3Class(frame);
+        final RStringVector storedClass = RArguments.getS3Args(frame) == null ? null : (RStringVector) RArguments.getS3Args(frame).clazz;
         if (storedClass == null) {
             return getAlternateClassHr(frame);
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java
index 1bc74df5ef82650e61fa7ebed46a58929325f4f2..8e68fd1b8e7449eeebc6b9cc82ba0cacb6e9bda3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java
@@ -94,7 +94,7 @@ public abstract class UpdateLength extends RInvisibleBuiltinNode {
 
     protected boolean isObject(VirtualFrame frame, RAbstractContainer container) {
         // if execution got here via S3 dispatch, treat objects as non-objects
-        return container.isObject(attrProfiles) && !RArguments.hasS3Args(frame);
+        return container.isObject(attrProfiles) && RArguments.getS3Args(frame) == null;
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index d668931becdb9bb7612f298757620095d06cf201..612abb1d7a21af3957056b873ff503a686de4cf5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -38,6 +38,7 @@ import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.nodes.instrument.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -54,7 +55,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     @Child private InlineCacheNode<VirtualFrame, RNode> onExitExpressionCache;
     private final ConditionProfile onExitProfile = ConditionProfile.createBinaryProfile();
 
-    private final ConditionProfile s3SlotsProfile = ConditionProfile.createBinaryProfile();
     @CompilationFinal private BranchProfile invalidateFrameSlotProfile;
     @Child private FrameSlotNode dotGenericSlot;
     @Child private FrameSlotNode dotMethodSlot;
@@ -168,9 +168,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         Object restartStack = RErrorHandling.getRestartStack();
         try {
             verifyEnclosingAssumptions(vf);
-            if (s3SlotsProfile.profile(RArguments.hasS3Args(vf))) {
-                setupS3Slots(vf);
-            }
+            setupS3Slots(vf);
             return body.execute(vf);
         } catch (ReturnException ex) {
             returnProfile.enter();
@@ -206,6 +204,10 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     }
 
     private void setupS3Slots(VirtualFrame frame) {
+        S3Args args = RArguments.getS3Args(frame);
+        if (args == null) {
+            return;
+        }
         if (dotGenericSlot == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             assert invalidateFrameSlotProfile == null && dotMethodSlot == null && dotClassSlot == null && dotGenericCallEnvSlot == null && dotGenericCallDefSlot == null && dotGroupSlot == null;
@@ -217,12 +219,12 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
             dotGenericCallDefSlot = insert(FrameSlotNode.create(RRuntime.RDotGenericDefEnv, true));
             dotGroupSlot = insert(FrameSlotNode.create(RRuntime.RDotGroup, true));
         }
-        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot.executeFrameSlot(frame), RArguments.getS3Generic(frame), false, invalidateFrameSlotProfile);
-        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot.executeFrameSlot(frame), RArguments.getS3Method(frame), false, invalidateFrameSlotProfile);
-        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot.executeFrameSlot(frame), RArguments.getS3Class(frame), false, invalidateFrameSlotProfile);
-        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot.executeFrameSlot(frame), RArguments.getS3CallEnv(frame), false, invalidateFrameSlotProfile);
-        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot.executeFrameSlot(frame), RArguments.getS3DefEnv(frame), false, invalidateFrameSlotProfile);
-        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot.executeFrameSlot(frame), RArguments.getS3Group(frame), false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot.executeFrameSlot(frame), args.generic, false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot.executeFrameSlot(frame), args.method, false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot.executeFrameSlot(frame), args.clazz, false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot.executeFrameSlot(frame), args.callEnv, false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot.executeFrameSlot(frame), args.defEnv, false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot.executeFrameSlot(frame), args.group, false, invalidateFrameSlotProfile);
     }
 
     @SuppressWarnings("unchecked")
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 717aef204d82d3e3aca183cb39c695adc82a878f..fe54c0e22c4d2fed13bafc5fe11bbdf2fc3dd83a 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
@@ -22,6 +22,7 @@ import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.nodes.runtime.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
@@ -361,13 +362,9 @@ class GroupDispatchNode extends S3DispatchLegacyNode {
 
     protected Object executeHelper(VirtualFrame frame, Object[] evaluatedArgs, ArgumentsSignature argumentsSignature) {
         EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argumentsSignature, this.hasVararg, this.callSrc);
-        Object[] argObject = RArguments.createS3Args(targetFunction, this.callSrc, null, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getSignature());
+        Object[] argObject = RArguments.create(targetFunction, this.callSrc, null, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getSignature());
         genCallEnv = frame.materialize();
-        defineVarsAsArguments(argObject, genericName, klass, genCallEnv, genDefEnv);
-        RArguments.setS3Method(argObject, dotMethod);
-        if (writeGroup) {
-            RArguments.setS3Group(argObject, groupName);
-        }
+        RArguments.setS3Args(argObject, new S3Args(genericName, klass, dotMethod, genCallEnv, genDefEnv, writeGroup ? groupName : null));
         indirectCallNode.assignSourceSection(this.callSrc);
         /*
          * Create a new frame s3VarDefFrame and define s3 generic variables such as .Generic,
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 723cccc47d4a007e6326c97c95da6cdd8c28c96a..49025ecfb2f7d8f58b7aad1808872e4e520278cf 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
@@ -17,15 +17,17 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode.ReadKind;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.data.*;
 
 public final class NextMethodDispatchNode extends S3DispatchLegacyNode {
 
-    @Child private ReadVariableNode rvnDefEnv;
-    @Child private ReadVariableNode rvnCallEnv;
-    @Child private ReadVariableNode rvnGroup;
-    @Child private ReadVariableNode rvnMethod;
+    @Child private ReadVariableNode rvnDefEnv = ReadVariableNode.create(RRuntime.RDotGenericDefEnv, RType.Any, ReadKind.SilentLocal);
+    @Child private ReadVariableNode rvnCallEnv = ReadVariableNode.create(RRuntime.RDotGenericCallEnv, RType.Any, ReadKind.SilentLocal);
+    @Child private ReadVariableNode rvnGroup = ReadVariableNode.create(RRuntime.RDotGroup, RType.Any, ReadKind.SilentLocal);
+    @Child private ReadVariableNode rvnMethod = ReadVariableNode.create(RRuntime.RDotMethod, RType.Any, ReadKind.SilentLocal);
     @Child private WriteVariableNode wvnGroup;
     private String group;
     private String lastGroup;
@@ -65,13 +67,11 @@ public final class NextMethodDispatchNode extends S3DispatchLegacyNode {
     private EvaluatedArguments processArgs(VirtualFrame frame) {
         int argsLength = args == null ? 0 : args.length;
         // Extract arguments from current frame...
-        int funArgsLength = RArguments.getArgumentsLength(frame);
         ArgumentsSignature signature = RArguments.getSignature(frame);
-        assert signature.getLength() == funArgsLength;
-        Object[] funArgValues = new Object[funArgsLength + argsLength];
-        String[] funArgNames = new String[funArgsLength + argsLength];
+        Object[] funArgValues = new Object[signature.getLength() + argsLength];
+        String[] funArgNames = new String[signature.getLength() + argsLength];
         int index = 0;
-        for (int fi = 0; fi < funArgsLength; fi++) {
+        for (int fi = 0; fi < signature.getLength(); fi++) {
             Object argVal = RArguments.getArgument(frame, fi);
             if (argVal instanceof RArgsValuesAndNames) {
                 RArgsValuesAndNames varArgs = (RArgsValuesAndNames) argVal;
@@ -115,16 +115,8 @@ public final class NextMethodDispatchNode extends S3DispatchLegacyNode {
 
     private Object executeHelper(VirtualFrame frame) {
         EvaluatedArguments evaledArgs = processArgs(frame);
-        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), null, RArguments.getDepth(frame) + 1, evaledArgs.getEvaluatedArgs(), evaledArgs.getSignature());
-        defineVarsAsArguments(argObject, genericName, klass, genCallEnv, genDefEnv);
-        if (storedFunctionName != null) {
-            RArguments.setS3Method(argObject, storedFunctionName);
-        } else {
-            RArguments.setS3Method(argObject, targetFunctionName);
-        }
-        if (hasGroup) {
-            RArguments.setS3Group(argObject, this.group);
-        }
+        Object[] argObject = RArguments.create(targetFunction, getSourceSection(), null, RArguments.getDepth(frame) + 1, evaledArgs.getEvaluatedArgs(), evaledArgs.getSignature());
+        RArguments.setS3Args(argObject, new S3Args(genericName, klass, storedFunctionName != null ? storedFunctionName : targetFunctionName, genCallEnv, genDefEnv, group));
         return indirectCallNode.call(frame, targetFunction.getTarget(), argObject);
     }
 
@@ -186,22 +178,23 @@ public final class NextMethodDispatchNode extends S3DispatchLegacyNode {
     }
 
     private void readGenericVars(VirtualFrame frame) {
-        genDefEnv = RArguments.getS3DefEnv(frame);
+        S3Args s3Args = RArguments.getS3Args(frame);
+        genDefEnv = s3Args == null ? null : s3Args.defEnv;
         if (genDefEnv == null) {
             genDefEnv = RArguments.getEnclosingFrame(frame);
         }
-        genCallEnv = RArguments.getS3CallEnv(frame);
+        genCallEnv = s3Args == null ? null : s3Args.callEnv;
         if (genCallEnv == null) {
             genCallEnv = frame.materialize();
         }
-        group = RArguments.getS3Group(frame);
+        group = s3Args == null ? null : s3Args.group;
         if (group == null || group.isEmpty()) {
             handleMissingGroup();
         } else {
             handlePresentGroup();
         }
 
-        Object method = RArguments.getS3Method(frame);
+        Object method = s3Args == null ? null : s3Args.method;
         String functionName;
         if (method == null) {
             functionName = null;
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 3d5779a6761882b8db1187cd745f5f41ffcf5588..4427f3fb1c0f667d1cf0157b1ad05ccf05e47339 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
@@ -23,6 +23,7 @@ import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.data.*;
 
 public abstract class S3DispatchNode extends DispatchNode {
@@ -135,9 +136,8 @@ public abstract class S3DispatchNode extends DispatchNode {
         CompilerAsserts.partialEvaluationConstant(evaluatedArguments.length);
 
         MaterializedFrame profiledCallerFrame = callerFrameProfile.profile(callerFrame);
-        Object[] argObject = RArguments.createS3Args(function, getSourceSection(), null, RArguments.getDepth(profiledCallerFrame) + 1, evaluatedArguments, evaluatedSignature);
-        defineVarsAsArguments(argObject, genericName, clazz, profiledCallerFrame.materialize(), genericDefFrame);
-        RArguments.setS3Method(argObject, functionName);
+        Object[] argObject = RArguments.create(function, getSourceSection(), null, RArguments.getDepth(profiledCallerFrame) + 1, evaluatedArguments, evaluatedSignature);
+        RArguments.setS3Args(argObject, new S3Args(genericName, clazz, functionName, profiledCallerFrame.materialize(), genericDefFrame, null));
         return argObject;
     }
 
@@ -150,13 +150,6 @@ public abstract class S3DispatchNode extends DispatchNode {
         return new StringBuilder(generic).append(RRuntime.RDOT).append(className).toString();
     }
 
-    protected static void defineVarsAsArguments(Object[] args, String genericName, RStringVector klass, MaterializedFrame genCallEnv, MaterializedFrame genDefEnv) {
-        RArguments.setS3Generic(args, genericName);
-        RArguments.setS3Class(args, klass);
-        RArguments.setS3CallEnv(args, genCallEnv);
-        RArguments.setS3DefEnv(args, genDefEnv);
-    }
-
     protected static final class TargetLookupResult {
         public final ReadVariableNode[] unsuccessfulReads;
         public final ReadVariableNode successfulRead;
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 0a32cc8b5bade4214f2cec3e22bf3164621f339b..91fb3016cfe409910a81308e4c2a04fd60bb5e78 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
@@ -84,32 +84,41 @@ public final class RArguments {
     @CompilationFinal public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
     @CompilationFinal public static final String[] EMPTY_STRING_ARRAY = new String[0];
 
+    public static final class S3Args {
+        public final String generic;
+        public final Object clazz;
+        public final Object method;
+        public final MaterializedFrame callEnv;
+        public final MaterializedFrame defEnv;
+        public final String group;
+
+        public S3Args(String generic, Object clazz, Object method, MaterializedFrame callEnv, MaterializedFrame defEnv, String group) {
+            assert generic != null && clazz != null && method != null && callEnv != null : generic + " " + clazz + " " + method + " " + callEnv;
+            this.generic = generic;
+            this.clazz = clazz;
+            this.method = method;
+            this.callEnv = callEnv;
+            this.defEnv = defEnv;
+            this.group = group;
+        }
+    }
+
     public static final int INDEX_ENVIRONMENT = 0;
     public static final int INDEX_FUNCTION = 1;
     public static final int INDEX_CALL_SRC = 2;
     public static final int INDEX_CALLER_FRAME = 3;
     public static final int INDEX_ENCLOSING_FRAME = 4;
-    public static final int INDEX_N_ARGS = 5;
+    public static final int INDEX_S3_ARGS = 5;
     public static final int INDEX_DEPTH = 6;
     public static final int INDEX_IS_IRREGULAR = 7;
     public static final int INDEX_SIGNATURE = 8;
     public static final int INDEX_ARGUMENTS = 9;
-    /*
-     * These indices are relative to INDEX_ARGUMENTS + nArgs+ nNames
-     */
-    private static final int S3_INDEX_GENERIC = 0;
-    private static final int S3_INDEX_CLASS = 1;
-    private static final int S3_INDEX_METHOD = 2;
-    private static final int S3_INDEX_CALL_ENV = 3;
-    private static final int S3_INDEX_DEF_ENV = 4;
-    private static final int S3_INDEX_GROUP = 5;
-    private static final int S3_VAR_COUNT = 6;
 
     /**
      * At the least, the array contains the function, enclosing frame, and numbers of arguments and
      * names.
      */
-    public static final int MINIMAL_ARRAY_LENGTH = INDEX_SIGNATURE + 1;
+    public static final int MINIMAL_ARRAY_LENGTH = INDEX_ARGUMENTS;
 
     private static final ValueProfile materializedFrameProfile = ValueProfile.createClassProfile();
 
@@ -146,11 +155,7 @@ public final class RArguments {
     }
 
     private static int getNArgs(Frame frame) {
-        return (int) getArgumentsWithEvalCheck(frame)[INDEX_N_ARGS];
-    }
-
-    private static int getS3StartIndex(Object[] args) {
-        return INDEX_ARGUMENTS + (int) args[INDEX_N_ARGS];
+        return getSignature(frame).getLength();
     }
 
     private static ArgumentsSignature getSignature(RFunction function) {
@@ -169,7 +174,6 @@ public final class RArguments {
         a[INDEX_ENCLOSING_FRAME] = enclosingFrame;
         a[INDEX_DEPTH] = depth;
         a[INDEX_IS_IRREGULAR] = false;
-        a[INDEX_N_ARGS] = evaluatedArgs.length;
         a[INDEX_SIGNATURE] = signature;
         copyArguments(evaluatedArgs, a, INDEX_ARGUMENTS);
         // assert envFunctionInvariant(a);
@@ -200,7 +204,6 @@ public final class RArguments {
     public static Object[] createUnitialized() {
         Object[] a = new Object[MINIMAL_ARRAY_LENGTH];
         a[INDEX_DEPTH] = 0;
-        a[INDEX_N_ARGS] = 0;
         a[INDEX_SIGNATURE] = ArgumentsSignature.empty(0);
         a[INDEX_IS_IRREGULAR] = false;
         return a;
@@ -224,118 +227,17 @@ public final class RArguments {
         return a;
     }
 
-    public static Object[] createS3Args(RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth, Object[] evaluatedArgs, ArgumentsSignature signature) {
-        Object[] a = new Object[MINIMAL_ARRAY_LENGTH + evaluatedArgs.length + S3_VAR_COUNT];
-        createHelper(a, null, functionObj, callSrc, callerFrame, depth, functionObj.getEnclosingFrameWithAssumption(), evaluatedArgs, signature);
-        return a;
-    }
-
-    public static boolean hasS3Args(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        if (args[INDEX_N_ARGS] == null) {
-            return false;
-        } else {
-            int s3StartIndex = getS3StartIndex(args);
-            return args.length > s3StartIndex;
-        }
-    }
-
-    public static String getS3Generic(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        return (String) args[s3StartIndex + S3_INDEX_GENERIC];
-    }
-
-    public static void setS3Generic(Object[] args, String generic) {
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        args[s3StartIndex + S3_INDEX_GENERIC] = generic;
-    }
-
     public static MaterializedFrame getCallerFrame(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         return (MaterializedFrame) args[INDEX_CALLER_FRAME];
     }
 
-    public static RStringVector getS3Class(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        int s3StartIndex = getS3StartIndex(args);
-        if (args.length <= s3StartIndex) {
-            return null;
-        } else {
-            return (RStringVector) args[s3StartIndex + S3_INDEX_CLASS];
-        }
-    }
-
-    public static void setS3Class(Object[] args, RStringVector klass) {
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        args[s3StartIndex + S3_INDEX_CLASS] = klass;
-    }
-
-    public static Object getS3Method(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        int s3StartIndex = getS3StartIndex(args);
-        if (args.length <= s3StartIndex) {
-            return null;
-        } else {
-            return args[s3StartIndex + S3_INDEX_METHOD];
-        }
-    }
-
-    public static void setS3Method(Object[] args, Object method) {
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        args[s3StartIndex + S3_INDEX_METHOD] = method;
-    }
-
-    public static MaterializedFrame getS3DefEnv(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        int s3StartIndex = getS3StartIndex(args);
-        if (args.length <= s3StartIndex) {
-            return null;
-        } else {
-            return (MaterializedFrame) args[s3StartIndex + S3_INDEX_DEF_ENV];
-        }
-    }
-
-    public static void setS3DefEnv(Object[] args, MaterializedFrame defEnv) {
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        args[s3StartIndex + S3_INDEX_DEF_ENV] = defEnv;
-    }
-
-    public static MaterializedFrame getS3CallEnv(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        int s3StartIndex = getS3StartIndex(args);
-        if (args.length <= s3StartIndex) {
-            return null;
-        } else {
-            return (MaterializedFrame) args[s3StartIndex + S3_INDEX_CALL_ENV];
-        }
-    }
-
-    public static void setS3CallEnv(Object[] args, MaterializedFrame callEnv) {
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        args[s3StartIndex + S3_INDEX_CALL_ENV] = callEnv;
-    }
-
-    public static String getS3Group(Frame frame) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
-        int s3StartIndex = getS3StartIndex(args);
-        if (args.length <= s3StartIndex) {
-            return null;
-        } else {
-            return (String) args[s3StartIndex + S3_INDEX_GROUP];
-        }
+    public static S3Args getS3Args(Frame frame) {
+        return (S3Args) getArgumentsWithEvalCheck(frame)[INDEX_S3_ARGS];
     }
 
-    public static void setS3Group(Object[] args, String group) {
-        int s3StartIndex = getS3StartIndex(args);
-        assert (args.length > s3StartIndex);
-        args[s3StartIndex + S3_INDEX_GROUP] = group;
+    public static void setS3Args(Object[] args, S3Args s3Args) {
+        args[INDEX_S3_ARGS] = s3Args;
     }
 
     public static REnvironment getEnvironment(Frame frame) {