From bd3fc8c77938bb358f3acfbd16ce98c0dccc4d66 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Mon, 30 Mar 2015 13:51:59 +0200
Subject: [PATCH] simplify and unify group generics implementation

---
 .../com/oracle/truffle/r/nodes/RASTUtils.java |  18 +-
 .../truffle/r/nodes/RTruffleVisitor.java      |   7 +-
 .../r/nodes/access/AccessArgumentNode.java    |   2 +
 .../array/write/UpdateArrayHelperNode.java    |   2 -
 .../r/nodes/function/ArgumentsNode.java       |   1 +
 .../function/BinaryOpsGroupDispatchNode.java  | 136 ------
 .../r/nodes/function/DispatchNode.java        |  43 --
 .../r/nodes/function/DispatchedCallNode.java  | 210 ---------
 .../nodes/function/GroupDispatchCallNode.java | 417 ------------------
 .../r/nodes/function/GroupDispatchNode.java   | 184 ++++++++
 .../function/NextMethodDispatchNode.java      | 225 ----------
 .../r/nodes/function/S3DispatchNode.java      | 348 ---------------
 .../function/UnaryOpsGroupDispatchNode.java   |  59 ---
 .../nodes/function/UseMethodDispatchNode.java | 355 ---------------
 .../truffle/r/nodes/runtime/RASTDeparse.java  |   4 +-
 .../r/nodes/runtime/RASTHelperImpl.java       |   4 +-
 .../oracle/truffle/r/runtime/RArguments.java  |   1 +
 .../com/oracle/truffle/r/runtime/RError.java  |   1 +
 .../truffle/r/runtime/RGroupGenerics.java     | 191 ++++----
 19 files changed, 285 insertions(+), 1923 deletions(-)
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/BinaryOpsGroupDispatchNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java

diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
index 8f4f5953ce..c0b9b05c59 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
@@ -191,9 +191,9 @@ public class RASTUtils {
             return RCallNode.createCall(null, RASTUtils.createReadVariableNode(((String) fn)), callArgsNode, null);
         } else if (fn instanceof ReadVariableNode) {
             return RCallNode.createCall(null, (ReadVariableNode) fn, callArgsNode, null);
-        } else if (fn instanceof GroupDispatchCallNode) {
-            GroupDispatchCallNode gdcn = (GroupDispatchCallNode) fn;
-            return GroupDispatchCallNode.create(gdcn.getGenericName(), gdcn.getGroupName(), callArgsNode, gdcn.getCallSrc());
+        } else if (fn instanceof GroupDispatchNode) {
+            GroupDispatchNode gdcn = (GroupDispatchNode) fn;
+            return GroupDispatchNode.create(gdcn.getGenericName(), gdcn.getGroup(), callArgsNode, gdcn.getCallSrc());
         } else {
             RFunction rfn = (RFunction) fn;
             return RCallNode.createCall(null, ConstantNode.create(rfn), callArgsNode, null);
@@ -201,7 +201,7 @@ public class RASTUtils {
     }
 
     /**
-     * Really should not be necessary, but things like '+' ({@link DispatchedCallNode}) have a
+     * Really should not be necessary, but things like '+' ({@link GroupDispatchNode}) have a
      * different AST structure from normal calls.
      */
     private static class CallArgsNodeFinder implements NodeVisitor {
@@ -234,7 +234,7 @@ public class RASTUtils {
 
     /**
      * Returns the name (as an {@link RSymbol} or the function associated with an {@link RCallNode}
-     * or {@link DispatchedCallNode}.
+     * or {@link GroupDispatchNode}.
      *
      * @param escape Add escape characters to non-standard names
      */
@@ -248,8 +248,8 @@ public class RASTUtils {
                 name = escapeName(name);
             }
             return RDataFactory.createSymbol(name);
-        } else if (child instanceof GroupDispatchCallNode) {
-            GroupDispatchCallNode groupDispatchNode = (GroupDispatchCallNode) child;
+        } else if (child instanceof GroupDispatchNode) {
+            GroupDispatchNode groupDispatchNode = (GroupDispatchNode) child;
             String gname = groupDispatchNode.getGenericName();
             if (escape) {
                 gname = escapeName(gname);
@@ -274,12 +274,12 @@ public class RASTUtils {
 
     /**
      * Returns the {@link ReadVariableNode} associated with a {@link RCallNode} or the
-     * {@link GroupDispatchCallNode} .
+     * {@link GroupDispatchNode} .
      */
     public static RNode findFunctionNode(Node node) {
         if (node instanceof RCallNode) {
             return ((RCallNode) node).getFunctionNode();
-        } else if (node instanceof GroupDispatchCallNode) {
+        } else if (node instanceof GroupDispatchNode) {
             return (RNode) node;
         }
         assert false;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
index fd77df9068..7b0b3614e3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
@@ -118,7 +118,7 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
         if (callName != null) {
             String functionName = callName;
             if (!FastROptions.DisableGroupGenerics.getValue() && RGroupGenerics.isGroupGeneric(functionName)) {
-                return GroupDispatchCallNode.create(functionName, RGroupGenerics.getGroup(functionName), aCallArgNode, callSource);
+                return GroupDispatchNode.create(functionName, RGroupGenerics.getGroup(functionName), aCallArgNode, callSource);
             }
             return RCallNode.createCall(callSource, ReadVariableNode.createForced(functionName, RType.Function), aCallArgNode, callParam);
         } else {
@@ -212,7 +212,8 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
         String functionName = op.getOperator().getName();
         CallArgumentsNode aCallArgNode = CallArgumentsNode.createUnnamed(false, true, operand);
         if (!FastROptions.DisableGroupGenerics.getValue() && RGroupGenerics.isGroupGeneric(functionName)) {
-            return GroupDispatchCallNode.create(functionName, RGroupGenerics.GROUP_OPS, aCallArgNode, op.getSource());
+            assert RGroupGenerics.getGroup(functionName) == RGroupGenerics.Ops;
+            return GroupDispatchNode.create(functionName, RGroupGenerics.Ops, aCallArgNode, op.getSource());
         }
         return RCallNode.createStaticCall(op.getSource(), functionName, aCallArgNode, op);
     }
@@ -227,7 +228,7 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
             String functionName = op.getOperator().getName();
             CallArgumentsNode aCallArgNode = CallArgumentsNode.createUnnamed(false, true, left, right);
             if (!FastROptions.DisableGroupGenerics.getValue() && RGroupGenerics.isGroupGeneric(functionName)) {
-                return GroupDispatchCallNode.create(functionName, RGroupGenerics.getGroup(functionName), aCallArgNode, op.getSource());
+                return GroupDispatchNode.create(functionName, RGroupGenerics.getGroup(functionName), aCallArgNode, op.getSource());
             }
             return RCallNode.createStaticCall(op.getSource(), functionName, aCallArgNode, op);
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
index 033997b1fc..daf546e131 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
@@ -31,6 +31,7 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
+import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.function.opt.*;
 import com.oracle.truffle.r.runtime.*;
@@ -93,6 +94,7 @@ public abstract class AccessArgumentNode extends RNode {
 
     @Specialization(guards = {"hasDefaultArg()"})
     protected Object doArgumentDefaultArg(VirtualFrame frame, @SuppressWarnings("unused") RMissing argMissing) {
+        assert !(getRootNode() instanceof RBuiltinRootNode) : getRootNode();
         Object result;
         if (canBeOptimized()) {
             // Insert default value
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java
index 4d294f138a..7bb262c5da 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/UpdateArrayHelperNode.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.Node.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.*;
@@ -38,7 +37,6 @@ import com.oracle.truffle.r.nodes.access.array.ArrayPositionCast.OperatorConvert
 import com.oracle.truffle.r.nodes.access.array.ArrayPositionCastNodeGen.OperatorConverterNodeGen;
 import com.oracle.truffle.r.nodes.access.array.read.*;
 import com.oracle.truffle.r.nodes.function.*;
-import com.oracle.truffle.r.nodes.function.DispatchedCallNode.*;
 import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RDeparse.State;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsNode.java
index b7ff42f2f0..1079a1da36 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsNode.java
@@ -47,6 +47,7 @@ public abstract class ArgumentsNode extends RNode implements ArgumentsTrait {
         assert signature != null && signature.getLength() == arguments.length : Arrays.toString(arguments) + " " + signature;
         this.arguments = arguments;
         this.signature = signature;
+        assert signature != null;
     }
 
     @CreateWrapper
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/BinaryOpsGroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/BinaryOpsGroupDispatchNode.java
deleted file mode 100644
index 4e7782eca3..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/BinaryOpsGroupDispatchNode.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014-2015, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.data.*;
-
-public class BinaryOpsGroupDispatchNode extends GroupDispatchNode {
-
-    private String targetFunctionNameR;
-    protected RFunction targetFunctionR;
-    private RStringVector klassR;
-    private RStringVector typeL;
-    private RStringVector typeR;
-    private boolean writeGroupR;
-    protected boolean isBuiltinCalled;
-
-    public BinaryOpsGroupDispatchNode(final String aGenericName, boolean hasVararg, SourceSection callSrc, SourceSection argSrc) {
-        super(aGenericName, RGroupGenerics.GROUP_OPS, hasVararg, callSrc, argSrc);
-    }
-
-    private void initDispatchTypes(final Object[] evaluatedArgs) {
-        // This is kind of tricky. We want to evaluate args before we know the function for which
-        // arguments should be matched. But as OpsGroupDispatchNode is for BinaryOperators, we can
-        // assume that arguments are in correct order!
-        this.typeL = getArgClass(evaluatedArgs[0]);
-        this.typeR = getArgClass(evaluatedArgs[1]);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
-        Object[] evaluatedArgs = argAndNames.getValues();
-        ArgumentsSignature signature = argAndNames.getSignature();
-        if (!isExecuted) {
-            isExecuted = true;
-            executeNoCache(frame, evaluatedArgs);
-        }
-        if (isBuiltinCalled || (targetFunctionR == null && targetFunction == null)) {
-            return callBuiltin(frame, evaluatedArgs, signature);
-        }
-        return executeHelper(frame, evaluatedArgs, signature);
-    }
-
-    @Override
-    public boolean isSameType(Object[] args) {
-        return !isExecuted || isEqualType(getArgClass(args[0]), this.typeL) && isEqualType(getArgClass(args[1]), this.typeR);
-    }
-
-    protected void executeNoCache(VirtualFrame frame, Object[] evaluatedArgs) {
-        initDispatchTypes(evaluatedArgs);
-        if (this.typeR != null) {
-            this.type = this.typeR;
-            findTargetFunction(frame);
-            targetFunctionNameR = targetFunctionName;
-            targetFunctionR = targetFunction;
-            klassR = klass;
-            writeGroupR = writeGroup;
-        } else {
-            targetFunctionR = null;
-        }
-        if (this.typeL != null) {
-            this.type = this.typeL;
-            findTargetFunction(frame);
-        } else {
-            targetFunction = null;
-        }
-        if (targetFunctionR == null && targetFunction == null) {
-            isBuiltinCalled = true;
-            return;
-        }
-        if (targetFunctionR != targetFunction) {
-            if (targetFunctionR != null && targetFunction != null) {
-                if (targetFunctionName.equals("Ops.difftime") && targetFunctionNameR.equals("+.POSIXt") && targetFunctionNameR.equals("+.Date")) {
-                    targetFunction = null;
-                } else if (!(targetFunctionNameR.equals("Ops.difftime") && targetFunctionName.equals("+.POSIXt") && targetFunctionName.equals("-.POSIXt") && targetFunctionNameR.equals("+.Date") && targetFunctionNameR.equals("-.Date"))) {
-                    /*
-                     * TODO: throw warning
-                     * "Incompatible methods (\"%s\", \"%s\") for \"%s\""),lname,rname, generic
-                     */
-                    isBuiltinCalled = true;
-                    return;
-                }
-            }
-            if (targetFunction == null) {
-                targetFunction = targetFunctionR;
-                targetFunctionName = targetFunctionNameR;
-                klass = klassR;
-                writeGroup = writeGroupR;
-                this.type = this.typeR;
-            }
-        }
-        String[] methods = new String[evaluatedArgs.length];
-        for (int i = 0; i < methods.length; ++i) {
-            methods[i] = "";
-            RStringVector classHr = getArgClass(evaluatedArgs[i]);
-            if (classHr == null) {
-                continue;
-            }
-            for (int j = 0; j < classHr.getLength(); ++j) {
-                if (classHr.getDataAt(j).equals(klass.getDataAt(0))) {
-                    methods[i] = targetFunctionName;
-                    break;
-                }
-            }
-        }
-        dotMethod = RDataFactory.createStringVector(methods, true);
-    }
-}
-
-class GenericBinarysOpsGroupDispatchNode extends BinaryOpsGroupDispatchNode {
-
-    public GenericBinarysOpsGroupDispatchNode(String aGenericName, boolean hasVararg, SourceSection callSrc, SourceSection argSrc) {
-        super(aGenericName, hasVararg, callSrc, argSrc);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
-        Object[] evaluatedArgs = argAndNames.getValues();
-        executeNoCache(frame, evaluatedArgs);
-        if (isBuiltinCalled || (targetFunctionR == null && targetFunction == null)) {
-            return callBuiltin(frame, evaluatedArgs, argAndNames.getSignature());
-        }
-        return executeHelper(frame, evaluatedArgs, argAndNames.getSignature());
-    }
-
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java
deleted file mode 100644
index 49a5359890..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.r.nodes.*;
-import com.oracle.truffle.r.nodes.function.DispatchedCallNode.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.data.*;
-
-public abstract class DispatchNode extends RNode {
-
-    protected final String genericName;
-
-    protected DispatchNode(String genericName) {
-        this.genericName = genericName;
-    }
-
-    public abstract Object executeGeneric(VirtualFrame frame, RStringVector aType);
-
-    @SuppressWarnings("unused")
-    public Object executeInternalGeneric(VirtualFrame frame, RStringVector aType, Object[] args) throws NoGenericMethodException {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    @SuppressWarnings("unused")
-    public Object executeInternal(VirtualFrame frame, Object[] args) throws NoGenericMethodException {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    public String getGenericName() {
-        return genericName;
-    }
-}
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
deleted file mode 100644
index 6a83bc2121..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.utilities.*;
-import com.oracle.truffle.r.nodes.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.data.*;
-
-public abstract class DispatchedCallNode extends RNode {
-
-    public static final class NoGenericMethodException extends ControlFlowException {
-        private static final long serialVersionUID = 344198853147758435L;
-    }
-
-    public static enum DispatchType {
-        UseMethod,
-        NextMethod
-    }
-
-    private static final int INLINE_CACHE_SIZE = 4;
-
-    public static DispatchedCallNode create(String genericName, DispatchType dispatchType, ArgumentsSignature signature) {
-        return new UninitializedDispatchedCallNode(genericName, dispatchType, signature);
-    }
-
-    public static DispatchedCallNode create(String genericName, String enclosingName, DispatchType dispatchType, Object[] args, ArgumentsSignature signature) {
-        return new UninitializedDispatchedCallNode(genericName, enclosingName, dispatchType, args, signature);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    public abstract Object execute(VirtualFrame frame, RStringVector type);
-
-    public abstract Object executeInternal(VirtualFrame frame, RStringVector type, Object[] args) throws NoGenericMethodException;
-
-    @Override
-    public boolean isSyntax() {
-        return true;
-    }
-
-    @NodeInfo(cost = NodeCost.UNINITIALIZED)
-    private static final class UninitializedDispatchedCallNode extends DispatchedCallNode {
-        private final int depth;
-        private final String genericName;
-        private final String enclosingName;
-        private final DispatchType dispatchType;
-        @CompilationFinal private final Object[] args;
-        private final ArgumentsSignature signature;
-
-        private UninitializedDispatchedCallNode(String genericName, String enclosingName, DispatchType dispatchType, Object[] args, ArgumentsSignature signature) {
-            this.genericName = genericName;
-            this.enclosingName = enclosingName;
-            this.signature = signature;
-            this.depth = 0;
-            this.dispatchType = dispatchType;
-            this.args = args;
-        }
-
-        public UninitializedDispatchedCallNode(String genericName, DispatchType dispatchType, ArgumentsSignature signature) {
-            this(genericName, null, dispatchType, null, signature);
-        }
-
-        private UninitializedDispatchedCallNode(UninitializedDispatchedCallNode copy, int depth) {
-            this.depth = depth;
-            this.genericName = copy.genericName;
-            this.enclosingName = copy.enclosingName;
-            this.dispatchType = copy.dispatchType;
-            this.signature = copy.signature;
-            this.args = null;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RStringVector type) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            return specialize(type).execute(frame, type);
-        }
-
-        @Override
-        public Object executeInternal(VirtualFrame frame, RStringVector type, @SuppressWarnings("hiding") Object[] args) throws NoGenericMethodException {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            return specialize(type).executeInternal(frame, type, args);
-        }
-
-        private DispatchedCallNode specialize(RStringVector type) {
-            CompilerAsserts.neverPartOfCompilation();
-            if (depth < INLINE_CACHE_SIZE) {
-                DispatchNode current = createCurrentNode(type, true);
-                return replace(new CachedNode(current, new UninitializedDispatchedCallNode(this, depth + 1), type));
-            }
-            RError.performanceWarning("S3 method dispatch fallback to generic");
-            return this.replace(new GenericDispatchNode(createCurrentNode(type, false)));
-        }
-
-        private DispatchNode createCurrentNode(RStringVector type, boolean cached) {
-            switch (dispatchType) {
-                case NextMethod:
-                    return new NextMethodDispatchNode(genericName, type, args, signature, enclosingName);
-                case UseMethod:
-                    return cached ? UseMethodDispatchNode.createCached(genericName, type, signature) : UseMethodDispatchNode.createGeneric(genericName, signature);
-                default:
-                    throw RInternalError.shouldNotReachHere();
-            }
-        }
-    }
-
-    private static final class GenericDispatchNode extends DispatchedCallNode {
-
-        @Child private DispatchNode dcn;
-
-        public GenericDispatchNode(DispatchNode dcn) {
-            this.dcn = dcn;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RStringVector type) {
-            return dcn.executeGeneric(frame, type);
-        }
-
-        @Override
-        public Object executeInternal(VirtualFrame frame, RStringVector type, Object[] args) throws NoGenericMethodException {
-            return dcn.executeInternalGeneric(frame, type, args);
-        }
-    }
-
-    private static final class CachedNode extends DispatchedCallNode {
-
-        private final ConditionProfile sameIdentityProfile = ConditionProfile.createBinaryProfile();
-        private final BranchProfile nullTypeProfile = BranchProfile.create();
-        private final BranchProfile lengthMismatch = BranchProfile.create();
-        private final BranchProfile notIdentityEqualElements = BranchProfile.create();
-
-        @Child private DispatchedCallNode nextNode;
-        @Child private DispatchNode currentNode;
-        private final RStringVector cachedType;
-        @CompilationFinal private final String[] cachedTypeElements;
-
-        CachedNode(DispatchNode currentNode, DispatchedCallNode nextNode, RStringVector type) {
-            this.nextNode = nextNode;
-            this.currentNode = currentNode;
-            this.cachedType = type;
-            this.cachedTypeElements = type.getDataCopy();
-        }
-
-        private boolean isEqualType(RStringVector type) {
-            if (sameIdentityProfile.profile(type == cachedType)) {
-                return true;
-            }
-            if (cachedType == null) {
-                return false;
-            }
-            if (type == null) {
-                nullTypeProfile.enter();
-                return false;
-            }
-            if (type.getLength() != cachedTypeElements.length) {
-                lengthMismatch.enter();
-                return false;
-            }
-            return compareLoop(type);
-        }
-
-        @ExplodeLoop
-        private boolean compareLoop(RStringVector type) {
-            for (int i = 0; i < cachedTypeElements.length; i++) {
-                String elementOne = cachedTypeElements[i];
-                String elementTwo = type.getDataAt(i);
-                if (elementOne != elementTwo) {
-                    notIdentityEqualElements.enter();
-                    if (!elementOne.equals(elementTwo)) {
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RStringVector type) {
-            if (isEqualType(type)) {
-                return currentNode.execute(frame);
-            }
-            return nextNode.execute(frame, type);
-        }
-
-        @Override
-        public Object executeInternal(VirtualFrame frame, RStringVector type, Object[] args) throws NoGenericMethodException {
-            if (isEqualType(type)) {
-                return currentNode.executeInternal(frame, args);
-            }
-            return nextNode.executeInternal(frame, type, args);
-        }
-    }
-}
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
deleted file mode 100644
index fe54c0e22c..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014, Purdue University
- * Copyright (c) 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import java.util.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.nodes.*;
-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.*;
-import com.oracle.truffle.r.runtime.env.*;
-
-import edu.umd.cs.findbugs.annotations.*;
-
-public abstract class GroupDispatchCallNode extends RNode {
-
-    private static final int INLINE_CACHE_SIZE = 4;
-    @Child protected CallArgumentsNode callArgsNode;
-
-    public static GroupDispatchCallNode create(String aGenericName, String groupName, CallArgumentsNode callArgNode, SourceSection callSrc) {
-        GroupDispatchCallNode gdcn = new UninitializedGroupDispatchCallNode(aGenericName, groupName, callArgNode);
-        gdcn.assignSourceSection(callSrc);
-        return gdcn;
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        throw new UnsupportedOperationException();
-    }
-
-    public abstract Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames);
-
-    public abstract String getGenericName();
-
-    public abstract String getGroupName();
-
-    public abstract SourceSection getCallSrc();
-
-    protected RArgsValuesAndNames evalArgs(VirtualFrame frame) {
-        UnrolledVariadicArguments unrolledArgs = callArgsNode.executeFlatten(frame);
-        RNode[] unevaledArgs = unrolledArgs.getArguments();
-        Object[] evaledArgs = new Object[unevaledArgs.length];
-        for (int i = 0; i < unevaledArgs.length; ++i) {
-            if (unevaledArgs[i] != null) {
-                evaledArgs[i] = unevaledArgs[i].execute(frame);
-            } else {
-                evaledArgs[i] = RMissing.instance;
-            }
-        }
-        // Delay assignment to allow recursion
-        RArgsValuesAndNames argAndNames = new RArgsValuesAndNames(evaledArgs, unrolledArgs.getSignature());
-        return argAndNames;
-    }
-
-    @Override
-    public boolean isSyntax() {
-        return true;
-    }
-
-    @Override
-    public void deparse(State state) {
-        String name = getGenericName();
-        RDeparse.Func func = RDeparse.getFunc(name);
-        if (func != null) {
-            // infix operator
-            RASTDeparse.deparseInfixOperator(state, this, func);
-        } else {
-            state.append(name);
-            this.callArgsNode.deparse(state);
-        }
-    }
-
-    @Override
-    public RNode substitute(REnvironment env) {
-        // TODO substitute aDispatchNode
-        return RASTUtils.createCall(this, (CallArgumentsNode) callArgsNode.substitute(env));
-    }
-
-    private static class UninitializedGroupDispatchCallNode extends GroupDispatchCallNode {
-
-        private final String groupName;
-        @CompilationFinal private final String genericName;
-        private final int depth;
-
-        public UninitializedGroupDispatchCallNode(String aGenericName, String groupName, CallArgumentsNode callArgNode) {
-            this.genericName = aGenericName;
-            this.groupName = groupName;
-            this.callArgsNode = callArgNode;
-            this.depth = 0;
-        }
-
-        private UninitializedGroupDispatchCallNode(UninitializedGroupDispatchCallNode copy, int depth) {
-            this.genericName = copy.genericName;
-            this.groupName = copy.groupName;
-            this.callArgsNode = copy.callArgsNode;
-            this.depth = depth;
-            this.assignSourceSection(copy.getSourceSection());
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            RArgsValuesAndNames argAndNames = evalArgs(frame);
-            return specialize(argAndNames).execute(frame, argAndNames);
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            return specialize(argAndNames).execute(frame, argAndNames);
-        }
-
-        private GroupDispatchCallNode specialize(RArgsValuesAndNames argAndNames) {
-            CompilerAsserts.neverPartOfCompilation();
-            if (depth < INLINE_CACHE_SIZE) {
-                final GroupDispatchNode current = createCurrentNode(argAndNames.getValues());
-                final GroupDispatchCallNode cachedNode = new CachedNode(current, new UninitializedGroupDispatchCallNode(this, this.depth + 1), this.callArgsNode);
-                this.replace(cachedNode);
-                return cachedNode;
-            }
-            return this.replace(new GenericDispatchNode(createGenericNode(argAndNames.getValues())));
-        }
-
-        private GroupDispatchNode createGenericNode(Object[] evaluatedArgs) {
-            if (this.groupName == RGroupGenerics.GROUP_OPS) {
-                if (evaluatedArgs.length == 1) {
-                    return new GenericUnaryOpsGroupDispatchNode(this.genericName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
-                }
-                if (evaluatedArgs.length >= 2) {
-                    return new GenericBinarysOpsGroupDispatchNode(this.genericName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(),
-                                    this.callArgsNode.getEncapsulatingSourceSection());
-                }
-            }
-            if (evaluatedArgs.length == 0 /*
-                                           * TODO add condition for when all the arguments are
-                                           * constant
-                                           */) {
-                return new GroupDispatchNode(this.genericName, this.groupName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
-            }
-            return new GenericGroupDispatchNode(this.genericName, this.groupName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
-        }
-
-        @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "GROUP_OPS is intended to be used as an identity")
-        protected GroupDispatchNode createCurrentNode(Object[] evaluatedArgs) {
-            if (this.groupName == RGroupGenerics.GROUP_OPS) {
-                if (evaluatedArgs.length == 1) {
-                    return new UnaryOpsGroupDispatchNode(this.genericName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
-                }
-                if (evaluatedArgs.length >= 2) {
-                    return new BinaryOpsGroupDispatchNode(this.genericName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
-                }
-            }
-            return new GroupDispatchNode(this.genericName, this.groupName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
-        }
-
-        @Override
-        public String getGenericName() {
-            return this.genericName;
-        }
-
-        @Override
-        public String getGroupName() {
-            return this.groupName;
-        }
-
-        @Override
-        public SourceSection getCallSrc() {
-            return this.getSourceSection();
-        }
-    }
-
-    private static final class CachedNode extends GroupDispatchCallNode {
-
-        @Child private GroupDispatchCallNode nextNode;
-        @Child private GroupDispatchNode currentNode;
-
-        CachedNode(final GroupDispatchNode currentNode, GroupDispatchCallNode nextNode, CallArgumentsNode callArgsNode) {
-            this.nextNode = nextNode;
-            this.currentNode = currentNode;
-            this.callArgsNode = callArgsNode;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            RArgsValuesAndNames argsAndNames = evalArgs(frame);
-            if (currentNode.isSameType(argsAndNames.getValues())) {
-                return currentNode.execute(frame, argsAndNames);
-            }
-            return nextNode.execute(frame, argsAndNames);
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
-            if (currentNode.isSameType(argAndNames.getValues())) {
-                return currentNode.execute(frame, argAndNames);
-            }
-            return nextNode.execute(frame, argAndNames);
-        }
-
-        @Override
-        public String getGenericName() {
-            return currentNode.genericName;
-        }
-
-        @Override
-        public String getGroupName() {
-            return currentNode.groupName;
-        }
-
-        @Override
-        public SourceSection getCallSrc() {
-            return currentNode.callSrc;
-        }
-    }
-
-    private static final class GenericDispatchNode extends GroupDispatchCallNode {
-
-        @Child private GroupDispatchNode gdn;
-
-        public GenericDispatchNode(GroupDispatchNode gdn) {
-            this.gdn = gdn;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
-            return gdn.execute(frame, argAndNames);
-        }
-
-        @Override
-        public String getGenericName() {
-            return gdn.genericName;
-        }
-
-        @Override
-        public String getGroupName() {
-            return gdn.groupName;
-        }
-
-        @Override
-        public SourceSection getCallSrc() {
-            return gdn.callSrc;
-        }
-    }
-}
-
-class GroupDispatchNode extends S3DispatchLegacyNode {
-
-    @CompilationFinal protected boolean isExecuted = false;
-    @CompilationFinal protected final String groupName;
-    @Child protected ReadVariableNode builtInNode;
-    @Child private WriteVariableNode wvnGroup;
-    protected RFunction builtinFunc;
-    protected boolean writeGroup;
-    protected RStringVector dotMethod;
-    protected boolean hasVararg;
-    protected final SourceSection callSrc;
-    protected final SourceSection argSrc;
-
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-    @Override
-    public Object executeGeneric(VirtualFrame frame, RStringVector aType) {
-        throw new AssertionError();
-    }
-
-    protected GroupDispatchNode(String genericName, String groupName, boolean hasVarArg, SourceSection callSrc, SourceSection argSrc) {
-        super(genericName, null);
-        this.groupName = groupName;
-        this.hasVararg = hasVarArg;
-        this.callSrc = callSrc;
-        this.argSrc = argSrc;
-    }
-
-    public boolean isSameType(Object[] args) {
-        return !isExecuted || isEqualType(getArgClass(args[0]), this.type);
-    }
-
-    protected void initBuiltin(VirtualFrame frame) {
-        // assuming builtin functions don't get redefined.
-        if (builtInNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            builtInNode = insert(ReadVariableNode.createFunctionLookup(genericName, true));
-            try {
-                builtinFunc = builtInNode.executeFunction(frame);
-            } catch (UnexpectedResultException e) {
-                throw new RuntimeException("Builtin " + this.genericName + " not found");
-            }
-        }
-    }
-
-    protected void findTargetFunction(VirtualFrame frame) {
-        String[] prefix = {genericName, groupName};
-        for (int i = 0; i < this.type.getLength(); ++i) {
-            for (int j = 0; j < prefix.length; ++j) {
-                findFunction(prefix[j], this.type.getDataAt(i), frame);
-                if (targetFunction != null) {
-                    RStringVector classVec = null;
-                    if (i > 0) {
-                        isFirst = false;
-                        classVec = RDataFactory.createStringVector(Arrays.copyOfRange(this.type.getDataWithoutCopying(), i, this.type.getLength()), true);
-                    } else {
-                        isFirst = true;
-                        classVec = this.type.copyResized(this.type.getLength(), false);
-                    }
-                    klass = classVec;
-                    if (j == 1) {
-                        writeGroup = true;
-                    } else {
-                        writeGroup = false;
-                    }
-                    return;
-                }
-            }
-        }
-    }
-
-    public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
-        Object[] evaluatedArgs = argAndNames.getValues();
-        ArgumentsSignature signature = argAndNames.getSignature();
-        if (!isExecuted) {
-            isExecuted = true;
-            this.type = evaluatedArgs.length > 0 ? getArgClass(evaluatedArgs[0]) : null;
-            if (this.type == null) {
-                return callBuiltin(frame, evaluatedArgs, signature);
-            }
-            findTargetFunction(frame);
-            if (targetFunction != null) {
-                dotMethod = RDataFactory.createStringVector(new String[]{targetFunctionName, ""}, true);
-            }
-        }
-        if (targetFunction == null) {
-            return callBuiltin(frame, evaluatedArgs, signature);
-        }
-        return executeHelper(frame, evaluatedArgs, signature);
-    }
-
-    protected Object callBuiltin(VirtualFrame frame, Object[] evaluatedArgs, ArgumentsSignature argumentsSignature) {
-        initBuiltin(frame);
-        EvaluatedArguments reorderedArgs = reorderArgs(frame, builtinFunc, evaluatedArgs, argumentsSignature, this.hasVararg, this.callSrc);
-        Object[] argObject = RArguments.create(builtinFunc, this.callSrc, null, RArguments.getDepth(frame), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getSignature());
-        indirectCallNode.assignSourceSection(this.callSrc);
-        return indirectCallNode.call(frame, builtinFunc.getTarget(), argObject);
-    }
-
-    protected Object executeHelper(VirtualFrame frame, Object[] evaluatedArgs, ArgumentsSignature argumentsSignature) {
-        EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argumentsSignature, this.hasVararg, this.callSrc);
-        Object[] argObject = RArguments.create(targetFunction, this.callSrc, null, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getSignature());
-        genCallEnv = frame.materialize();
-        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,
-         * .Method etc. in it and set this frame as the enclosing frame of the target function
-         * ensuring that these generic variables are available to the called function. The real
-         * enclosing frame of the target function become enclosing frame of the new frame
-         * s3VarDefFrame. After the function returns reset the enclosing frame of the target
-         * function.
-         */
-        Object result = indirectCallNode.call(frame, targetFunction.getTarget(), argObject);
-        return result;
-    }
-
-    protected RStringVector getArgClass(Object arg) {
-        if (arg instanceof RAbstractContainer && ((RAbstractContainer) arg).isObject(attrProfiles)) {
-            return ((RAbstractContainer) arg).getClassHierarchy();
-        }
-        return null;
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        throw new AssertionError();
-    }
-}
-
-class GenericGroupDispatchNode extends GroupDispatchNode {
-
-    protected GenericGroupDispatchNode(String aGenericName, String groupName, boolean hasVarArg, SourceSection callSrc, SourceSection argSrc) {
-        super(aGenericName, groupName, hasVarArg, callSrc, argSrc);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
-        Object[] evaluatedArgs = argAndNames.getValues();
-        ArgumentsSignature signature = argAndNames.getSignature();
-        this.type = getArgClass(evaluatedArgs[0]);
-        if (this.type == null) {
-            return callBuiltin(frame, evaluatedArgs, signature);
-        }
-        findTargetFunction(frame);
-        if (targetFunction != null) {
-            dotMethod = RDataFactory.createStringVector(new String[]{targetFunctionName, ""}, true);
-        }
-        if (targetFunction == null) {
-            callBuiltin(frame, evaluatedArgs, signature);
-        }
-        return executeHelper(frame, evaluatedArgs, signature);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
new file mode 100644
index 0000000000..28b1c7ad4c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java
@@ -0,0 +1,184 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2014, Purdue University
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.nodes.function;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.NoGenericMethodException;
+import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
+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.*;
+import com.oracle.truffle.r.runtime.env.*;
+
+public final class GroupDispatchNode extends RNode {
+
+    @Child private CallArgumentsNode callArgsNode;
+    @Child private S3FunctionLookupNode functionLookupL;
+    @Child private S3FunctionLookupNode functionLookupR;
+    @Child private CallMatcherNode callMatcher = CallMatcherNode.create(false, true);
+
+    private final String genericName;
+    private final RGroupGenerics group;
+    private final RFunction builtinFunc;
+    private final boolean binaryLookup;
+
+    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    private final ConditionProfile mismatchProfile = ConditionProfile.createBinaryProfile();
+
+    public GroupDispatchNode(String genericName, RGroupGenerics group, CallArgumentsNode callArgNode) {
+        this.genericName = genericName.intern();
+        this.group = group;
+        this.callArgsNode = callArgNode;
+        this.binaryLookup = group == RGroupGenerics.Ops && callArgsNode.getSignature().getLength() >= 2;
+
+        this.builtinFunc = RContext.getEngine().lookupBuiltin(genericName);
+    }
+
+    public static GroupDispatchNode create(String aGenericName, RGroupGenerics group, CallArgumentsNode callArgNode, SourceSection callSrc) {
+        GroupDispatchNode gdcn = new GroupDispatchNode(aGenericName, group, callArgNode);
+        gdcn.assignSourceSection(callSrc);
+        return gdcn;
+    }
+
+    public String getGenericName() {
+        return genericName;
+    }
+
+    public RGroupGenerics getGroup() {
+        return group;
+    }
+
+    public SourceSection getCallSrc() {
+        return getSourceSection();
+    }
+
+    protected RArgsValuesAndNames evalArgs(VirtualFrame frame) {
+        UnrolledVariadicArguments unrolledArgs = callArgsNode.executeFlatten(frame);
+        RNode[] unevaledArgs = unrolledArgs.getArguments();
+        Object[] evaledArgs = new Object[unevaledArgs.length];
+        for (int i = 0; i < unevaledArgs.length; ++i) {
+            if (unevaledArgs[i] != null) {
+                evaledArgs[i] = unevaledArgs[i].execute(frame);
+            } else {
+                evaledArgs[i] = RMissing.instance;
+            }
+        }
+        // Delay assignment to allow recursion
+        return new RArgsValuesAndNames(evaledArgs, unrolledArgs.getSignature());
+    }
+
+    @Override
+    public boolean isSyntax() {
+        return true;
+    }
+
+    @Override
+    public void deparse(State state) {
+        String name = getGenericName();
+        RDeparse.Func func = RDeparse.getFunc(name);
+        if (func != null) {
+            // infix operator
+            RASTDeparse.deparseInfixOperator(state, this, func);
+        } else {
+            state.append(name);
+            callArgsNode.deparse(state);
+        }
+    }
+
+    @Override
+    public RNode substitute(REnvironment env) {
+        // TODO substitute aDispatchNode
+        return RASTUtils.createCall(this, (CallArgumentsNode) callArgsNode.substitute(env));
+    }
+
+    protected RStringVector getArgClass(Object arg) {
+        if (arg instanceof RAbstractContainer && ((RAbstractContainer) arg).isObject(attrProfiles)) {
+            return ((RAbstractContainer) arg).getClassHierarchy();
+        }
+        return null;
+    }
+
+    @Override
+    public Object execute(VirtualFrame frame) {
+        RArgsValuesAndNames argAndNames = evalArgs(frame);
+        Object[] evaluatedArgs = argAndNames.getValues();
+
+        RStringVector typeL = evaluatedArgs.length == 0 ? null : getArgClass(evaluatedArgs[0]);
+
+        Result resultL = null;
+        if (typeL != null) {
+            try {
+                if (functionLookupL == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    functionLookupL = insert(S3FunctionLookupNode.create(false, false));
+                }
+                resultL = functionLookupL.execute(frame, genericName, typeL, group.getName(), frame.materialize(), null);
+            } catch (NoGenericMethodException e) {
+                // fall-through
+            }
+        }
+        Result resultR = null;
+        if (binaryLookup) {
+            RStringVector typeR = getArgClass(evaluatedArgs[1]);
+            if (typeR != null) {
+                try {
+                    if (functionLookupR == null) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        functionLookupR = insert(S3FunctionLookupNode.create(false, false));
+                    }
+                    resultR = functionLookupR.execute(frame, genericName, typeR, group.getName(), frame.materialize(), null);
+                } catch (NoGenericMethodException e) {
+                    // fall-through
+                }
+            }
+        }
+
+        Result result;
+        RStringVector dotMethod;
+        if (resultL == null) {
+            if (resultR == null) {
+                result = null;
+                dotMethod = null;
+            } else {
+                result = resultR;
+                dotMethod = RDataFactory.createStringVector(new String[]{"", result.targetFunctionName}, true);
+            }
+        } else {
+            if (resultR == null) {
+                result = resultL;
+                dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, ""}, true);
+            } else {
+                if (mismatchProfile.profile(resultL.function != resultR.function)) {
+                    RError.warning(getSourceSection(), RError.Message.INCOMPATIBLE_METHODS, resultL.targetFunctionName, resultR.targetFunctionName, genericName);
+                    result = null;
+                    dotMethod = null;
+                } else {
+                    result = resultL;
+                    dotMethod = RDataFactory.createStringVector(new String[]{result.targetFunctionName, result.targetFunctionName}, true);
+                }
+            }
+        }
+        ArgumentsSignature signature = argAndNames.getSignature();
+        if (result == null) {
+            return callMatcher.execute(frame, signature, evaluatedArgs, builtinFunc, null);
+        } else {
+            S3Args s3Args = new S3Args(genericName, result.clazz, dotMethod, frame.materialize(), null, result.groupMatch ? group.getName() : null);
+            return callMatcher.execute(frame, signature, evaluatedArgs, result.function, s3Args);
+        }
+    }
+}
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
deleted file mode 100644
index 49025ecfb2..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-
-package com.oracle.truffle.r.nodes.function;
-
-import java.util.*;
-
-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 = 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;
-    private String storedFunctionName;
-    private String lastStoredFunctionName;
-    private String baseName;
-    private String[] prefix;
-    private boolean hasGroup;
-    private boolean lastHasGroup;
-    @CompilationFinal private final Object[] args;
-
-    NextMethodDispatchNode(String genericName, RStringVector type, Object[] args, ArgumentsSignature suppliedSignature, String storedFunctionName) {
-        super(genericName, suppliedSignature);
-        this.type = type;
-        this.args = args;
-        this.storedFunctionName = storedFunctionName;
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        readGenericVars(frame);
-        if (!isSame() || !isFirst) {
-            findTargetFunction(frame);
-            storeValues();
-        }
-        return executeHelper(frame);
-    }
-
-    @Override
-    public Object executeGeneric(VirtualFrame frame, RStringVector aType) {
-        readGenericVars(frame);
-        findTargetFunction(frame);
-        storeValues();
-        return executeHelper(frame);
-    }
-
-    private EvaluatedArguments processArgs(VirtualFrame frame) {
-        int argsLength = args == null ? 0 : args.length;
-        // Extract arguments from current frame...
-        ArgumentsSignature signature = RArguments.getSignature(frame);
-        Object[] funArgValues = new Object[signature.getLength() + argsLength];
-        String[] funArgNames = new String[signature.getLength() + argsLength];
-        int index = 0;
-        for (int fi = 0; fi < signature.getLength(); fi++) {
-            Object argVal = RArguments.getArgument(frame, fi);
-            if (argVal instanceof RArgsValuesAndNames) {
-                RArgsValuesAndNames varArgs = (RArgsValuesAndNames) argVal;
-                int varArgsLength = varArgs.length();
-                if (varArgsLength != 1) {
-                    funArgValues = Utils.resizeArray(funArgValues, funArgValues.length + varArgsLength - 1);
-                }
-                System.arraycopy(varArgs.getValues(), 0, funArgValues, index, varArgsLength);
-                if (varArgsLength != 1) {
-                    funArgNames = Utils.resizeArray(funArgNames, funArgNames.length + varArgsLength - 1);
-                }
-                for (int i = 0; i < varArgsLength; i++) {
-                    funArgNames[index++] = varArgs.getSignature().getName(i);
-                }
-            } else {
-                funArgValues[index] = argVal;
-                funArgNames[index] = signature.getName(fi);
-                index++;
-            }
-        }
-        if (argsLength > 0) {
-            for (int i = 0; i < argsLength; i++) {
-                funArgValues[index] = args[i];
-                if (suppliedSignature != null) {
-                    funArgNames[index] = suppliedSignature.getName(i);
-                }
-                index++;
-            }
-        }
-
-        ArgumentsSignature evaluatedSignature = ArgumentsSignature.get(funArgNames);
-
-        EvaluatedArguments evaledArgs = EvaluatedArguments.create(funArgValues, evaluatedSignature);
-        // ...to match them against the chosen function's formal arguments
-        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(targetFunction, evaledArgs, getSourceSection(), true);
-        if (targetFunction.isBuiltin()) {
-            ArgumentMatcher.evaluatePromises(frame, promiseHelper, reorderedArgs.getEvaluatedArgs());
-        }
-        return reorderedArgs;
-    }
-
-    private Object executeHelper(VirtualFrame frame) {
-        EvaluatedArguments evaledArgs = processArgs(frame);
-        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);
-    }
-
-    private boolean isSame() {
-        return lastHasGroup == hasGroup && isEqual(lastGroup, group) && isEqual(lastStoredFunctionName, storedFunctionName);
-    }
-
-    private static boolean isEqual(String a, String b) {
-        if (a == null || b == null) {
-            return a == null && b == null;
-        }
-        return a.equals(b);
-    }
-
-    private void findTargetFunction(VirtualFrame frame) {
-        int nextClassIndex = 0;
-        String currentFunctionName = storedFunctionName == null ? RArguments.getFunction(frame).getName() : storedFunctionName;
-        for (int i = 0; i < type.getLength(); ++i) {
-            if (RRuntime.toString(new StringBuffer(baseName).append(RRuntime.RDOT).append(type.getDataAt(i))).equals(currentFunctionName)) {
-                nextClassIndex = i + 1;
-                break;
-            }
-        }
-        final int firstClassIndex = nextClassIndex;
-        int index = 0;
-        // First try generic.class then group.class.
-        for (; nextClassIndex < type.getLength() && targetFunction == null; ++nextClassIndex) {
-            for (; index < prefix.length && targetFunction == null; findFunction(prefix[index++], type.getDataAt(nextClassIndex), genCallEnv)) {
-            }
-        }
-        if (firstClassIndex == nextClassIndex && index == 1) {
-            isFirst = true;
-        } else {
-            isFirst = false;
-        }
-        if (targetFunction == null) {
-            findFunction(this.genericName, RRuntime.DEFAULT, genCallEnv);
-        }
-        if (targetFunction == null) {
-            findFunction(this.genericName, frame);
-            if (targetFunction == null || !targetFunction.isBuiltin()) {
-                throw RError.error(getEncapsulatingSourceSection(), RError.Message.NO_METHOD_FOUND);
-            }
-        }
-        RStringVector classVec = null;
-        if (nextClassIndex == type.getLength()) {
-            classVec = RDataFactory.createStringVector("");
-        } else {
-            classVec = RDataFactory.createStringVector(Arrays.copyOfRange(type.getDataWithoutCopying(), nextClassIndex, type.getLength()), true);
-        }
-        classVec.setAttr(RRuntime.PREVIOUS_ATTR_KEY, type.copyResized(type.getLength(), false));
-        klass = classVec;
-    }
-
-    private void storeValues() {
-        lastHasGroup = hasGroup;
-        lastGroup = group;
-        lastStoredFunctionName = storedFunctionName;
-    }
-
-    private void readGenericVars(VirtualFrame frame) {
-        S3Args s3Args = RArguments.getS3Args(frame);
-        genDefEnv = s3Args == null ? null : s3Args.defEnv;
-        if (genDefEnv == null) {
-            genDefEnv = RArguments.getEnclosingFrame(frame);
-        }
-        genCallEnv = s3Args == null ? null : s3Args.callEnv;
-        if (genCallEnv == null) {
-            genCallEnv = frame.materialize();
-        }
-        group = s3Args == null ? null : s3Args.group;
-        if (group == null || group.isEmpty()) {
-            handleMissingGroup();
-        } else {
-            handlePresentGroup();
-        }
-
-        Object method = s3Args == null ? null : s3Args.method;
-        String functionName;
-        if (method == null) {
-            functionName = null;
-        } else if (method instanceof String) {
-            functionName = (String) method;
-        } else {
-            functionName = ((RStringVector) method).getDataAt(0);
-        }
-        if (functionName != null) {
-            storedFunctionName = functionName;
-        }
-    }
-
-    private void handleMissingGroup() {
-        baseName = genericName;
-        prefix = new String[1];
-        prefix[0] = genericName;
-        hasGroup = false;
-    }
-
-    private void handlePresentGroup() {
-        baseName = group;
-        prefix = new String[2];
-        prefix[0] = genericName;
-        prefix[1] = group;
-        hasGroup = true;
-    }
-}
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
deleted file mode 100644
index 4427f3fb1c..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-
-package com.oracle.truffle.r.nodes.function;
-
-import java.util.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
-import com.oracle.truffle.api.nodes.*;
-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 {
-
-    @Child protected PromiseHelperNode promiseHelper = new PromiseHelperNode();
-
-    protected final BranchProfile errorProfile = BranchProfile.create();
-    private final ConditionProfile topLevelFrameProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile callerFrameSlotPath = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile hasVarArgsProfile = ConditionProfile.createBinaryProfile();
-    private final ValueProfile argumentCountProfile = ValueProfile.createPrimitiveProfile();
-
-    protected final ArgumentsSignature suppliedSignature;
-
-    public S3DispatchNode(String genericName, ArgumentsSignature suppliedSignature) {
-        super(genericName);
-        this.suppliedSignature = suppliedSignature;
-    }
-
-    protected MaterializedFrame getCallerFrame(VirtualFrame frame) {
-        MaterializedFrame funFrame = RArguments.getCallerFrame(frame);
-        if (callerFrameSlotPath.profile(funFrame == null)) {
-            funFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE).materialize();
-            RError.performanceWarning("slow caller frame access in UseMethod dispatch");
-        }
-        // S3 method can be dispatched from top-level where there is no caller frame
-        return topLevelFrameProfile.profile(funFrame == null) ? frame.materialize() : funFrame;
-    }
-
-    protected Object[] extractArguments(VirtualFrame frame, boolean fixedLength) {
-        int argCount = argumentCountProfile.profile(RArguments.getArgumentsLength(frame));
-        Object[] argValues = new Object[argCount];
-        if (fixedLength) {
-            extractArgumentsLoopFixedLength(frame, argCount, argValues);
-        } else {
-            extractArgumentsLoop(frame, argCount, argValues);
-        }
-        return argValues;
-    }
-
-    private static void extractArgumentsLoop(VirtualFrame frame, int argCount, Object[] argValues) {
-        for (int i = 0; i < argCount; i++) {
-            argValues[i] = RArguments.getArgument(frame, i);
-        }
-    }
-
-    @ExplodeLoop
-    private static void extractArgumentsLoopFixedLength(VirtualFrame frame, int argCount, Object[] argValues) {
-        for (int i = 0; i < argCount; i++) {
-            argValues[i] = RArguments.getArgument(frame, i);
-        }
-    }
-
-    protected EvaluatedArguments reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, SourceSection errorSourceSection) {
-        assert paramSignature.getLength() == args.length;
-
-        int argCount = args.length;
-        int argListSize = argCount;
-
-        boolean hasVarArgs = false;
-        for (int fi = 0; fi < argCount; ++fi) {
-            Object arg = args[fi];
-            if (hasVarArgsProfile.profile(arg instanceof RArgsValuesAndNames)) {
-                hasVarArgs = true;
-                argListSize += ((RArgsValuesAndNames) arg).length() - 1;
-            }
-        }
-        Object[] argValues;
-        ArgumentsSignature signature;
-        if (hasVarArgs) {
-            argValues = new Object[argListSize];
-            String[] argNames = new String[argListSize];
-            int index = 0;
-            for (int fi = 0; fi < argCount; ++fi) {
-                Object arg = args[fi];
-                if (arg instanceof RArgsValuesAndNames) {
-                    RArgsValuesAndNames varArgs = (RArgsValuesAndNames) arg;
-                    Object[] varArgValues = varArgs.getValues();
-                    ArgumentsSignature varArgSignature = varArgs.getSignature();
-                    for (int i = 0; i < varArgs.length(); i++) {
-                        argNames[index] = varArgSignature.getName(i);
-                        argValues[index++] = checkMissing(varArgValues[i]);
-                    }
-                } else {
-                    argNames[index] = paramSignature.getName(fi);
-                    argValues[index++] = checkMissing(arg);
-                }
-            }
-            signature = ArgumentsSignature.get(argNames);
-        } else {
-            argValues = new Object[argCount];
-            for (int i = 0; i < argCount; i++) {
-                argValues[i] = checkMissing(args[i]);
-            }
-            signature = paramSignature;
-        }
-
-        // ...and use them as 'supplied' arguments...
-        EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, signature);
-
-        // ...to match them against the chosen function's formal arguments
-        EvaluatedArguments evaluated = ArgumentMatcher.matchArgumentsEvaluated(function, evaledArgs, errorSourceSection, false);
-        return evaluated;
-    }
-
-    private final ValueProfile callerFrameProfile = ValueProfile.createClassProfile();
-
-    protected final Object[] prepareArguments(MaterializedFrame callerFrame, MaterializedFrame genericDefFrame, Object[] evaluatedArguments, ArgumentsSignature evaluatedSignature, RFunction function,
-                    RStringVector clazz, String functionName) {
-        CompilerAsserts.partialEvaluationConstant(evaluatedArguments.length);
-
-        MaterializedFrame profiledCallerFrame = callerFrameProfile.profile(callerFrame);
-        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;
-    }
-
-    protected static Object checkMissing(Object value) {
-        return RMissingHelper.isMissing(value) || (value instanceof RPromise && RMissingHelper.isMissingName((RPromise) value)) ? null : value;
-    }
-
-    @TruffleBoundary
-    protected static String functionName(String generic, String className) {
-        return new StringBuilder(generic).append(RRuntime.RDOT).append(className).toString();
-    }
-
-    protected static final class TargetLookupResult {
-        public final ReadVariableNode[] unsuccessfulReads;
-        public final ReadVariableNode successfulRead;
-        public final RFunction targetFunction;
-        public final String targetFunctionName;
-        public final RStringVector clazz;
-
-        public TargetLookupResult(ReadVariableNode[] unsuccessfulReads, ReadVariableNode successfulRead, RFunction targetFunction, String targetFunctionName, RStringVector clazz) {
-            this.unsuccessfulReads = unsuccessfulReads;
-            this.successfulRead = successfulRead;
-            this.targetFunction = targetFunction;
-            this.targetFunctionName = targetFunctionName;
-            this.clazz = clazz;
-        }
-    }
-
-    protected static TargetLookupResult findTargetFunctionLookup(Frame lookupFrame, RStringVector type, String genericName, boolean createReadVariableNodes) {
-        CompilerAsserts.neverPartOfCompilation();
-        RFunction targetFunction = null;
-        String targetFunctionName = null;
-        RStringVector clazz = null;
-        ArrayList<ReadVariableNode> unsuccessfulReads = createReadVariableNodes ? new ArrayList<>() : null;
-
-        for (int i = 0; i <= type.getLength(); i++) {
-            String clazzName = i == type.getLength() ? RRuntime.DEFAULT : type.getDataAt(i);
-            String functionName = genericName + RRuntime.RDOT + clazzName;
-            ReadVariableNode rvn;
-            Object func;
-            if (createReadVariableNodes) {
-                rvn = ReadVariableNode.createFunctionLookup(functionName, false);
-                func = rvn.execute(null, lookupFrame);
-            } else {
-                rvn = null;
-                func = ReadVariableNode.lookupFunction(functionName, lookupFrame);
-            }
-            if (func != null) {
-                assert func instanceof RFunction;
-                targetFunctionName = functionName;
-                targetFunction = (RFunction) func;
-
-                if (i == 0) {
-                    clazz = type.copyResized(type.getLength(), false);
-                } else if (i == type.getLength()) {
-                    clazz = null;
-                } else {
-                    clazz = RDataFactory.createStringVector(Arrays.copyOfRange(type.getDataWithoutCopying(), i, type.getLength()), true);
-                    clazz.setAttr(RRuntime.PREVIOUS_ATTR_KEY, type.copyResized(type.getLength(), false));
-                }
-                ReadVariableNode[] array = createReadVariableNodes ? unsuccessfulReads.toArray(new ReadVariableNode[unsuccessfulReads.size()]) : null;
-                return new TargetLookupResult(array, rvn, targetFunction, targetFunctionName, clazz);
-            } else {
-                if (createReadVariableNodes) {
-                    unsuccessfulReads.add(rvn);
-                }
-            }
-        }
-        if (createReadVariableNodes) {
-            return new TargetLookupResult(unsuccessfulReads.toArray(new ReadVariableNode[unsuccessfulReads.size()]), null, null, null, null);
-        } else {
-            return null;
-        }
-    }
-}
-
-abstract class S3DispatchLegacyNode extends S3DispatchNode {
-
-    protected RStringVector type;
-
-    @Child protected IndirectCallNode indirectCallNode = Truffle.getRuntime().createIndirectCallNode();
-    protected RStringVector klass;
-    protected MaterializedFrame genCallEnv;
-    protected MaterializedFrame genDefEnv;
-    protected boolean isFirst;
-
-    @CompilationFinal private String lastFun;
-    @Child private ReadVariableNode lookup;
-    protected String targetFunctionName;
-    protected RFunction targetFunction;
-
-    public S3DispatchLegacyNode(String genericName, ArgumentsSignature suppliedSignature) {
-        super(genericName, suppliedSignature);
-    }
-
-    protected void findFunction(String functionName, Frame frame) {
-        if (lookup == null || !functionName.equals(lastFun)) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            lastFun = functionName;
-            ReadVariableNode rvn = ReadVariableNode.createFunctionLookup(functionName, false);
-            lookup = lookup == null ? insert(rvn) : lookup.replace(rvn);
-        }
-        targetFunction = null;
-        targetFunctionName = null;
-        Object func;
-        if (frame instanceof VirtualFrame) {
-            func = lookup.execute((VirtualFrame) frame);
-        } else {
-            func = lookup.execute(null, frame);
-        }
-        if (func != null) {
-            assert func instanceof RFunction;
-            targetFunctionName = functionName;
-            targetFunction = (RFunction) func;
-        }
-    }
-
-    protected void findFunction(String generic, String className, Frame frame) {
-        checkLength(className, generic);
-        findFunction(functionName(generic, className), frame);
-    }
-
-    private void checkLength(String className, String generic) {
-        // The magic number two taken from src/main/objects.c
-        if (className.length() + generic.length() + 2 > RRuntime.LEN_METHOD_NAME) {
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.TOO_LONG_CLASS_NAME, generic);
-        }
-    }
-
-    protected EvaluatedArguments reorderArgs(VirtualFrame frame, RFunction func, Object[] evaluatedArgs, ArgumentsSignature signature, boolean hasVarArgs, SourceSection callSrc) {
-        Object[] evaluatedArgsValues = evaluatedArgs;
-        ArgumentsSignature evaluatedSignature;
-        int argCount = evaluatedArgs.length;
-        if (hasVarArgs) {
-            evaluatedSignature = signature;
-        } else {
-            String[] evaluatedArgNames = null;
-            evaluatedArgNames = new String[signature.getLength()];
-            int fi = 0;
-            int index = 0;
-            int argListSize = evaluatedArgsValues.length;
-            for (; fi < argCount; ++fi) {
-                Object arg = evaluatedArgs[fi];
-                if (arg instanceof RArgsValuesAndNames) {
-                    RArgsValuesAndNames varArgsContainer = (RArgsValuesAndNames) arg;
-                    argListSize += varArgsContainer.length() - 1;
-                    evaluatedArgsValues = Utils.resizeArray(evaluatedArgsValues, argListSize);
-                    // argNames can be null if no names for arguments have been specified
-                    evaluatedArgNames = evaluatedArgNames == null ? new String[argListSize] : Utils.resizeArray(evaluatedArgNames, argListSize);
-                    Object[] varArgsValues = varArgsContainer.getValues();
-                    for (int i = 0; i < varArgsContainer.length(); i++) {
-                        evaluatedArgsValues[index] = checkMissing(varArgsValues[i]);
-                        String name = varArgsContainer.getSignature().getName(i);
-                        evaluatedArgNames[index] = name;
-                        index++;
-                    }
-                } else {
-                    evaluatedArgsValues[index] = checkMissing(arg);
-                    evaluatedArgNames[index] = signature.getName(fi);
-                    index++;
-                }
-            }
-            evaluatedSignature = ArgumentsSignature.get(evaluatedArgNames);
-        }
-        EvaluatedArguments evaledArgs = EvaluatedArguments.create(evaluatedArgsValues, evaluatedSignature);
-        // ...to match them against the chosen function's formal arguments
-        EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(func, evaledArgs, callSrc, false);
-        if (func.isBuiltin()) {
-            ArgumentMatcher.evaluatePromises(frame, promiseHelper, reorderedArgs.getEvaluatedArgs());
-        }
-        return reorderedArgs;
-    }
-
-    public static boolean isEqualType(RStringVector one, RStringVector two) {
-        if (one == null && two == null) {
-            return true;
-        }
-        if (one == null || two == null) {
-            return false;
-        }
-
-        if (one.getLength() != two.getLength()) {
-            return false;
-        }
-        for (int i = 0; i < one.getLength(); i++) {
-            if (!one.getDataAt(i).equals(two.getDataAt(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
-
-abstract class S3DispatchCachedNode extends S3DispatchNode {
-    protected final RStringVector type;
-
-    public S3DispatchCachedNode(String genericName, RStringVector type, ArgumentsSignature suppliedSignature) {
-        super(genericName, suppliedSignature);
-        this.type = type;
-    }
-}
-
-abstract class S3DispatchGenericNode extends S3DispatchNode {
-
-    public S3DispatchGenericNode(String genericName, ArgumentsSignature suppliedSignature) {
-        super(genericName, suppliedSignature);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java
deleted file mode 100644
index d95ca5c21e..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014-2015, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.data.*;
-
-/*
- * Handles unary +, - and ! operators.
- */
-public class UnaryOpsGroupDispatchNode extends GroupDispatchNode {
-
-    public UnaryOpsGroupDispatchNode(String genericName, boolean hasVararg, SourceSection callSrc, SourceSection argSrc) {
-        super(genericName, RGroupGenerics.GROUP_OPS, hasVararg, callSrc, argSrc);
-    }
-
-    @Override
-    protected Object callBuiltin(VirtualFrame frame, Object[] evaluatedArgs, ArgumentsSignature signature) {
-        initBuiltin(frame);
-        Object[] args = ((HasSignature) builtinFunc.getRootNode()).getSignature().getLength() == 1 ? new Object[]{evaluatedArgs[0]} : new Object[]{evaluatedArgs[0], RMissing.instance};
-        Object[] argObject = RArguments.create(builtinFunc, callSrc, null, RArguments.getDepth(frame) + 1, args);
-        return indirectCallNode.call(frame, builtinFunc.getTarget(), argObject);
-    }
-}
-
-class GenericUnaryOpsGroupDispatchNode extends UnaryOpsGroupDispatchNode {
-
-    public GenericUnaryOpsGroupDispatchNode(String genericName, boolean hasVararg, SourceSection callSrc, SourceSection argSrc) {
-        super(genericName, hasVararg, callSrc, argSrc);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
-        Object[] evaluatedArgs = argAndNames.getValues();
-        ArgumentsSignature signature = argAndNames.getSignature();
-        this.type = getArgClass(evaluatedArgs[0]);
-        if (this.type == null) {
-            return callBuiltin(frame, evaluatedArgs, signature);
-        }
-        findTargetFunction(frame);
-        if (targetFunction != null) {
-            dotMethod = RDataFactory.createStringVector(new String[]{targetFunctionName, ""}, true);
-        }
-        if (targetFunction == null) {
-            callBuiltin(frame, evaluatedArgs, signature);
-        }
-        return executeHelper(frame, evaluatedArgs, signature);
-    }
-}
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
deleted file mode 100644
index a188769bea..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-
-package com.oracle.truffle.r.nodes.function;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.nodes.*;
-import com.oracle.truffle.r.nodes.access.variables.*;
-import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation;
-import com.oracle.truffle.r.nodes.function.DispatchedCallNode.NoGenericMethodException;
-import com.oracle.truffle.r.runtime.*;
-import com.oracle.truffle.r.runtime.data.*;
-
-/**
- * {@code UseMethod} is typically called like this:
- *
- * <pre>
- * f <- function(x, ...) UseMethod("f")
- * </pre>
- *
- * Locating the correct call depends on the class of {@code x}, and the search starts in the
- * enclosing (parent) environment of {@code f}, which, for packages, which is where most of these
- * definitions occur, will be the package {@code namepace} enviromnent.
- */
-public abstract class UseMethodDispatchNode {
-
-    public static DispatchNode createCached(String genericName, RStringVector type, ArgumentsSignature suppliedSignature) {
-        return new UseMethodDispatchCachedNode(genericName, type, suppliedSignature);
-    }
-
-    public static DispatchNode createGeneric(String genericName, ArgumentsSignature suppliedSignature) {
-        return new UseMethodDispatchGenericNode(genericName, suppliedSignature);
-    }
-}
-
-final class UseMethodDispatchCachedNode extends S3DispatchCachedNode {
-
-    @NodeInfo(cost = NodeCost.NONE)
-    private static final class CheckReadsNode extends Node {
-        @Children private final ReadVariableNode[] unsuccessfulReadsCallerFrame;
-        @Children private final ReadVariableNode[] unsuccessfulReadsDefFrame;
-        // if readsDefFrame != null, then this read will go to the def frame
-        @Child private ReadVariableNode successfulRead;
-
-        private final ArgumentsSignature signature;
-        @CompilationFinal private final ArgumentsSignature[] varArgSignature;
-
-        public final RFunction function;
-        public final RStringVector clazz;
-        public final String functionName;
-        @CompilationFinal public long[] preparePermutation;
-        public final MatchPermutation permutation;
-
-        public CheckReadsNode(ReadVariableNode[] unsuccessfulReadsCallerFrame, ReadVariableNode[] unsuccessfulReadsDefFrame, ReadVariableNode successfulRead, RFunction function, RStringVector clazz,
-                        String functionName, ArgumentsSignature signature, ArgumentsSignature[] varArgSignature, long[] preparePermutation, MatchPermutation permutation) {
-            this.unsuccessfulReadsCallerFrame = unsuccessfulReadsCallerFrame;
-            this.unsuccessfulReadsDefFrame = unsuccessfulReadsDefFrame;
-            this.successfulRead = successfulRead;
-            this.function = function;
-            this.clazz = clazz;
-            this.functionName = functionName;
-            this.signature = signature;
-            this.varArgSignature = varArgSignature;
-            this.preparePermutation = preparePermutation;
-            this.permutation = permutation;
-        }
-
-        public boolean executeReads(Frame callerFrame, MaterializedFrame defFrame, ArgumentsSignature actualSignature, Object[] actualArguments) {
-            if (actualSignature != signature) {
-                return false;
-            }
-            if (!checkLastArgSignature(actualArguments)) {
-                return false;
-            }
-            if (!executeReads(unsuccessfulReadsCallerFrame, callerFrame)) {
-                return false;
-            }
-            Object actualFunction;
-            if (unsuccessfulReadsDefFrame != null) {
-                if (!executeReads(unsuccessfulReadsDefFrame, defFrame)) {
-                    return false;
-                }
-                actualFunction = successfulRead.execute(null, defFrame);
-            } else {
-                actualFunction = successfulRead.execute(null, callerFrame);
-            }
-            return actualFunction == function;
-        }
-
-        @ExplodeLoop
-        private static boolean executeReads(ReadVariableNode[] reads, Frame callerFrame) {
-            for (ReadVariableNode read : reads) {
-                if (read.execute(null, callerFrame) != null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @ExplodeLoop
-        private boolean checkLastArgSignature(Object[] arguments) {
-            for (int i = 0; i < arguments.length; i++) {
-                Object arg = arguments[i];
-                if (arg instanceof RArgsValuesAndNames) {
-                    if (varArgSignature == null || varArgSignature[i] != ((RArgsValuesAndNames) arg).getSignature()) {
-                        return false;
-                    }
-                } else {
-                    if (varArgSignature != null && varArgSignature[i] != null) {
-                        return false;
-                    }
-                }
-            }
-            return true;
-        }
-    }
-
-    @Child private CheckReadsNode cached;
-    @Child private DirectCallNode call;
-
-    public UseMethodDispatchCachedNode(String genericName, RStringVector type, ArgumentsSignature suppliedSignature) {
-        super(genericName, type, suppliedSignature);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        Object[] arguments = extractArguments(frame, true);
-        ArgumentsSignature signature = RArguments.getSignature(frame);
-        MaterializedFrame genericDefFrame = RArguments.getEnclosingFrame(frame);
-        MaterializedFrame callerFrame = getCallerFrame(frame);
-
-        if (cached == null || !cached.executeReads(callerFrame, genericDefFrame, signature, arguments)) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            specialize(frame, callerFrame, signature, arguments, true);
-        }
-
-        Object[] preparedArguments = prepareSuppliedArgument(cached.preparePermutation, arguments);
-
-        RRootNode rootNode = (RRootNode) cached.function.getTarget().getRootNode();
-        FormalArguments formals = rootNode.getFormalArguments();
-        ArgumentsSignature formalSignature = formals.getSignature();
-
-        Object[] reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(cached.permutation, preparedArguments, formals);
-
-        CompilerAsserts.partialEvaluationConstant(reorderedArgs.length);
-
-        if (cached.function.isBuiltin()) {
-            ArgumentMatcher.evaluatePromises(frame, promiseHelper, reorderedArgs);
-        }
-
-        Object[] argObject = prepareArguments(callerFrame, genericDefFrame, reorderedArgs, formalSignature, cached.function, cached.clazz, cached.functionName);
-
-        return call.call(frame, argObject);
-    }
-
-    @ExplodeLoop
-    private static Object[] prepareSuppliedArgument(long[] preparePermutation, Object[] arguments) {
-        Object[] result = new Object[preparePermutation.length];
-        for (int i = 0; i < result.length; i++) {
-            long source = preparePermutation[i];
-            if (source >= 0) {
-                result[i] = arguments[(int) source];
-            } else {
-                source = -source;
-                result[i] = ((RArgsValuesAndNames) arguments[(int) (source >> 32)]).getValues()[(int) source];
-            }
-        }
-        return result;
-    }
-
-    private void specialize(Frame callerFrame, MaterializedFrame genericDefFrame, ArgumentsSignature signature, Object[] arguments, boolean throwsRError) {
-        CompilerAsserts.neverPartOfCompilation();
-        // look for a match in the caller frame hierarchy
-        TargetLookupResult result = findTargetFunctionLookup(callerFrame, type, genericName, true);
-        ReadVariableNode[] unsuccessfulReadsCaller = result.unsuccessfulReads;
-        ReadVariableNode[] unsuccessfulReadsDef = null;
-        if (result.successfulRead == null) {
-            if (genericDefFrame != null) {
-                // look for a match in the generic def frame hierarchy
-                result = findTargetFunctionLookup(genericDefFrame, type, genericName, true);
-                unsuccessfulReadsDef = result.unsuccessfulReads;
-            }
-            if (result.successfulRead == null) {
-                if (throwsRError) {
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.UNKNOWN_FUNCTION_USE_METHOD, genericName, type);
-                } else {
-                    throw new NoGenericMethodException();
-                }
-            }
-        }
-
-        int argCount = arguments.length;
-        int argListSize = argCount;
-
-        // extract vararg signatures from the arguments
-        ArgumentsSignature[] varArgSignatures = null;
-        for (int i = 0; i < arguments.length; i++) {
-            Object arg = arguments[i];
-            if (arg instanceof RArgsValuesAndNames) {
-                if (varArgSignatures == null) {
-                    varArgSignatures = new ArgumentsSignature[arguments.length];
-                }
-                varArgSignatures[i] = ((RArgsValuesAndNames) arg).getSignature();
-                argListSize += ((RArgsValuesAndNames) arg).length() - 1;
-            }
-        }
-
-        long[] preparePermutation;
-        ArgumentsSignature resultSignature;
-        if (varArgSignatures != null) {
-            resultSignature = ArgumentsSignature.flattenNames(signature, varArgSignatures, argListSize);
-            preparePermutation = ArgumentsSignature.flattenIndexes(varArgSignatures, argListSize);
-        } else {
-            preparePermutation = new long[argCount];
-            for (int i = 0; i < argCount; i++) {
-                preparePermutation[i] = i;
-            }
-            resultSignature = signature;
-        }
-
-        assert resultSignature != null;
-        ArgumentsSignature formalSignature = ArgumentMatcher.getFunctionSignature(result.targetFunction);
-        MatchPermutation permutation = ArgumentMatcher.matchArguments(resultSignature, formalSignature, getEncapsulatingSourceSection(), false);
-
-        CheckReadsNode newCheckedReads = new CheckReadsNode(unsuccessfulReadsCaller, unsuccessfulReadsDef, result.successfulRead, result.targetFunction, result.clazz, result.targetFunctionName,
-                        signature, varArgSignatures, preparePermutation, permutation);
-        DirectCallNode newCall = Truffle.getRuntime().createDirectCallNode(result.targetFunction.getTarget());
-        if (call == null) {
-            cached = insert(newCheckedReads);
-            call = insert(newCall);
-        } else {
-            RError.performanceWarning("re-specializing UseMethodDispatchCachedNode");
-            cached.replace(newCheckedReads);
-            call.replace(newCall);
-        }
-    }
-
-    @Override
-    public Object executeGeneric(VirtualFrame frame, RStringVector aType) {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    @Override
-    public Object executeInternal(VirtualFrame frame, Object[] arguments) throws NoGenericMethodException {
-        ArgumentsSignature signature = suppliedSignature;
-        if (cached == null || !cached.executeReads(frame, null, signature, arguments)) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            specialize(frame, null, signature, arguments, false);
-        }
-        EvaluatedArguments reorderedArgs = reorderArguments(arguments, cached.function, suppliedSignature, getEncapsulatingSourceSection());
-        if (cached.function.isBuiltin()) {
-            ArgumentMatcher.evaluatePromises(frame, promiseHelper, reorderedArgs.getEvaluatedArgs());
-        }
-        Object[] argObject = prepareArguments(frame.materialize(), null, reorderedArgs.arguments, reorderedArgs.signature, cached.function, cached.clazz, cached.functionName);
-        return call.call(frame, argObject);
-    }
-
-    @Override
-    public Object executeInternalGeneric(VirtualFrame frame, RStringVector aType, Object[] args) throws NoGenericMethodException {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-}
-
-final class UseMethodDispatchGenericNode extends S3DispatchGenericNode {
-
-    @Child protected IndirectCallNode indirectCallNode = Truffle.getRuntime().createIndirectCallNode();
-
-    public UseMethodDispatchGenericNode(String genericName, ArgumentsSignature suppliedSignature) {
-        super(genericName, suppliedSignature);
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    @Override
-    public Object executeGeneric(VirtualFrame frame, RStringVector type) {
-        Object[] arguments = extractArguments(frame, false);
-        ArgumentsSignature signature = RArguments.getSignature(frame);
-        MaterializedFrame genericDefFrame = RArguments.getEnclosingFrame(frame);
-        MaterializedFrame callerFrame = getCallerFrame(frame);
-
-        TargetLookupResult lookupResult = findTargetFunction(callerFrame, genericDefFrame, type, true);
-        Object[] callArguments = executeHelper(frame, callerFrame, genericDefFrame, lookupResult, arguments, signature, getSourceSection());
-        return indirectCallNode.call(frame, lookupResult.targetFunction.getTarget(), callArguments);
-    }
-
-    @Override
-    public Object executeInternal(VirtualFrame frame, Object[] args) {
-        throw RInternalError.shouldNotReachHere();
-    }
-
-    @Override
-    public Object executeInternalGeneric(VirtualFrame frame, RStringVector type, Object[] arguments) {
-        MaterializedFrame genericDefFrame = RArguments.getEnclosingFrame(frame);
-        MaterializedFrame callerFrame = getCallerFrame(frame);
-
-        TargetLookupResult lookupResult = findTargetFunction(callerFrame, genericDefFrame, type, true);
-        Object[] callArguments = executeHelper(frame, callerFrame, genericDefFrame, lookupResult, arguments, suppliedSignature, getEncapsulatingSourceSection());
-        return indirectCallNode.call(frame, lookupResult.targetFunction.getTarget(), callArguments);
-    }
-
-    private Object[] executeHelper(VirtualFrame frame, MaterializedFrame callerFrame, MaterializedFrame genericDefFrame, TargetLookupResult lookupResult, Object[] args,
-                    ArgumentsSignature paramSignature, SourceSection errorSourceSection) {
-        RFunction function = lookupResult.targetFunction;
-        EvaluatedArguments reorderedArgs = reorderArguments(args, function, paramSignature, errorSourceSection);
-        if (function.isBuiltin()) {
-            ArgumentMatcher.evaluatePromises(frame, promiseHelper, reorderedArgs.getEvaluatedArgs());
-        }
-        return prepareArguments(callerFrame, genericDefFrame, lookupResult, function, reorderedArgs);
-    }
-
-    @Override
-    @TruffleBoundary
-    protected EvaluatedArguments reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, SourceSection errorSourceSection) {
-        return super.reorderArguments(args, function, paramSignature, errorSourceSection);
-    }
-
-    @TruffleBoundary
-    private Object[] prepareArguments(MaterializedFrame callerFrame, MaterializedFrame genericDefFrame, TargetLookupResult lookupResult, RFunction function, EvaluatedArguments reorderedArgs) {
-        return super.prepareArguments(callerFrame, genericDefFrame, reorderedArgs.arguments, reorderedArgs.signature, function, lookupResult.clazz, lookupResult.targetFunctionName);
-    }
-
-    @TruffleBoundary
-    private TargetLookupResult findTargetFunction(MaterializedFrame callerFrame, MaterializedFrame genericDefFrame, RStringVector type, boolean throwsRError) {
-        TargetLookupResult lookupResult = findTargetFunctionLookup(callerFrame, type, genericName, false);
-        if (lookupResult == null) {
-            lookupResult = findTargetFunctionLookup(genericDefFrame, type, genericName, false);
-            if (lookupResult == null) {
-                if (throwsRError) {
-                    throw RError.error(getEncapsulatingSourceSection(), RError.Message.UNKNOWN_FUNCTION_USE_METHOD, genericName, RRuntime.toString(type));
-                } else {
-                    throw new NoGenericMethodException();
-                }
-            }
-        }
-        return lookupResult;
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java
index 4caaaec408..9f20193bcb 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTDeparse.java
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1995-2012, The R Core Team
  * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -52,7 +52,7 @@ public class RASTDeparse {
     }
 
     private static Func isInfixOperatorNode(Node node) {
-        if (node instanceof RCallNode || node instanceof GroupDispatchCallNode) {
+        if (node instanceof RCallNode || node instanceof GroupDispatchNode) {
             Object fname = RASTUtils.findFunctionName(node, false);
             return isInfixOperator(fname);
         } else {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java
index 05b653c514..e99cad9a26 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java
@@ -87,7 +87,7 @@ public class RASTHelperImpl implements RASTHelper {
     @TruffleBoundary
     private static int computeLength(Node node) {
         int result = 1;
-        if (node instanceof RCallNode || node instanceof DispatchedCallNode || node instanceof GroupDispatchCallNode) {
+        if (node instanceof RCallNode || node instanceof GroupDispatchNode) {
             // 1 + number of args
             CallArgumentsNode args = RASTUtils.findCallArgumentsNode(node);
             result += args.getArguments().length;
@@ -137,7 +137,7 @@ public class RASTHelperImpl implements RASTHelper {
         // index has already been range checked based on computeLength
         Node node = RASTUtils.unwrap(rl.getRep());
         int index = indexArg;
-        if (node instanceof RCallNode || node instanceof DispatchedCallNode || node instanceof GroupDispatchCallNode) {
+        if (node instanceof RCallNode || node instanceof GroupDispatchNode) {
             if (index == 0) {
                 return RASTUtils.findFunctionName(node, true);
             } else {
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 4e5168730d..4a1749be22 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
@@ -94,6 +94,7 @@ public final class RArguments {
 
         public S3Args(String generic, Object clazz, Object method, MaterializedFrame callEnv, MaterializedFrame defEnv, String group) {
             assert generic != null && callEnv != null : generic + " " + callEnv;
+            assert generic.intern() == generic;
             this.generic = generic;
             this.clazz = clazz;
             this.method = method;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 30c1a5fd6a..e948eab02e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -449,6 +449,7 @@ public final class RError extends RuntimeException {
         INVALID_TYPE_LENGTH("invalid type/length (%s/%d) in vector allocation"),
         SUBASSIGN_TYPE_FIX("incompatible types (from %s to %s) in subassignment type fix"),
         SUBSCRIPT_TYPES("incompatible types (from %s to %s) in [[ assignment"),
+        INCOMPATIBLE_METHODS("incompatible methods (\"%s\", \"%s\") for \"%s\""),
         RECURSIVE_INDEXING_FAILED("recursive indexing failed at level %d"),
         ARGUMENTS_PASSED("%d arguments passed to '%s' which requires %d"),
         ARGUMENTS_PASSED_0_1("0 arguments passed to '%s' which requires 1"),
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RGroupGenerics.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RGroupGenerics.java
index cb31c420d5..95e86e618b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RGroupGenerics.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RGroupGenerics.java
@@ -10,121 +10,88 @@
  */
 package com.oracle.truffle.r.runtime;
 
-import java.util.*;
-
-public class RGroupGenerics {
-
-    public static final String GROUP_MATH = "Math";
-
-    public static final String GROUP_OPS = "Ops";
-
-    public static final String GROUP_SUMMARY = "Summary";
-
-    public static final String GROUP_COMPLEX = "Complex";
-
-    private static Map<String, String> methodToGroup;
-
-    static {
-        initializeMethodToGroup();
-    }
-
-    /*
-     * Returns the group to which a given S-3 group generic method belongs to.
-     */
-    public static String getGroup(final String methodName) {
-        return methodToGroup.get(methodName);
+public enum RGroupGenerics {
+    Math,
+    Ops,
+    Summary,
+    Complex;
+
+    public String getName() {
+        return name();
     }
 
-    /*
-     * Returns true if the method is group generic.
-     */
-    public static boolean isGroupGeneric(final String methodName) {
-        return methodToGroup.containsKey(methodName);
+    public static RGroupGenerics getGroup(String methodName) {
+        switch (methodName) {
+            case "abs":
+            case "sign":
+            case "sqrt":
+            case "floor":
+            case "ceiling":
+            case "trunc":
+            case "round":
+            case "signif":
+            case "exp":
+            case "log":
+            case "expm1":
+            case "log1p":
+            case "cos":
+            case "sin":
+            case "tan":
+            case "acos":
+            case "asin":
+            case "atan":
+            case "cosh":
+            case "sinh":
+            case "tanh":
+            case "acosh":
+            case "asinh":
+            case "atanh":
+            case "lgamma":
+            case "gamma":
+            case "digamma":
+            case "trigamma":
+            case "cumsum":
+            case "cumprod":
+            case "cummax":
+            case "cummin":
+                return Math;
+            case "+":
+            case "-":
+            case "*":
+            case "/":
+            case "^":
+            case "%%":
+            case "%/%":
+            case "&":
+            case "|":
+            case "!":
+            case "==":
+            case "!=":
+            case "<":
+            case "<=":
+            case ">=":
+            case ">":
+                return Ops;
+            case "max":
+            case "min":
+            case "prod":
+            case "sum":
+            case "all":
+            case "any":
+            case "range":
+                return Summary;
+            case "Arg":
+            case "Conj":
+            case "Im":
+            case "Mod":
+            case "Re":
+                return Complex;
+            default:
+                return null;
+        }
     }
 
-    /*
-     * S3 methods can be written for four groups:"Math", "Ops", "Summary" and "Complex". The
-     * following method maps each method to its associated group.
-     */
-    private static void initializeMethodToGroup() {
-        assert (methodToGroup == null);
-        methodToGroup = new HashMap<>();
-        addGroupMath();
-        addGroupOps();
-        addGroupSummary();
-        addGroupComplex();
+    public static boolean isGroupGeneric(String methodName) {
+        return getGroup(methodName) != null;
     }
-
-    private static void addGroupMath() {
-        methodToGroup.put("abs", GROUP_MATH);
-        methodToGroup.put("sign", GROUP_MATH);
-        methodToGroup.put("sqrt", GROUP_MATH);
-        methodToGroup.put("floor", GROUP_MATH);
-        methodToGroup.put("ceiling", GROUP_MATH);
-        methodToGroup.put("trunc", GROUP_MATH);
-        methodToGroup.put("round", GROUP_MATH);
-        methodToGroup.put("signif", GROUP_MATH);
-        methodToGroup.put("exp", GROUP_MATH);
-        methodToGroup.put("log", GROUP_MATH);
-        methodToGroup.put("expm1", GROUP_MATH);
-        methodToGroup.put("log1p", GROUP_MATH);
-        methodToGroup.put("cos", GROUP_MATH);
-        methodToGroup.put("sin", GROUP_MATH);
-        methodToGroup.put("tan", GROUP_MATH);
-        methodToGroup.put("acos", GROUP_MATH);
-        methodToGroup.put("asin", GROUP_MATH);
-        methodToGroup.put("atan", GROUP_MATH);
-        methodToGroup.put("cosh", GROUP_MATH);
-        methodToGroup.put("sinh", GROUP_MATH);
-        methodToGroup.put("tanh", GROUP_MATH);
-        methodToGroup.put("acosh", GROUP_MATH);
-        methodToGroup.put("asinh", GROUP_MATH);
-        methodToGroup.put("atanh", GROUP_MATH);
-        methodToGroup.put("lgamma", GROUP_MATH);
-        methodToGroup.put("gamma", GROUP_MATH);
-        methodToGroup.put("digamma", GROUP_MATH);
-        methodToGroup.put("trigamma", GROUP_MATH);
-        methodToGroup.put("cumsum", GROUP_MATH);
-        methodToGroup.put("cumprod", GROUP_MATH);
-        methodToGroup.put("cummax", GROUP_MATH);
-        methodToGroup.put("cummin", GROUP_MATH);
-    }
-
-    private static void addGroupOps() {
-        methodToGroup.put("+", GROUP_OPS);
-        methodToGroup.put("-", GROUP_OPS);
-        methodToGroup.put("*", GROUP_OPS);
-        methodToGroup.put("/", GROUP_OPS);
-        methodToGroup.put("^", GROUP_OPS);
-        methodToGroup.put("%%", GROUP_OPS);
-        methodToGroup.put("%/%", GROUP_OPS);
-        methodToGroup.put("&", GROUP_OPS);
-        methodToGroup.put("|", GROUP_OPS);
-        methodToGroup.put("!", GROUP_OPS);
-        methodToGroup.put("==", GROUP_OPS);
-        methodToGroup.put("!=", GROUP_OPS);
-        methodToGroup.put("<", GROUP_OPS);
-        methodToGroup.put("<=", GROUP_OPS);
-        methodToGroup.put(">=", GROUP_OPS);
-        methodToGroup.put(">", GROUP_OPS);
-    }
-
-    private static void addGroupSummary() {
-        methodToGroup.put("max", GROUP_SUMMARY);
-        methodToGroup.put("min", GROUP_SUMMARY);
-        methodToGroup.put("prod", GROUP_SUMMARY);
-        methodToGroup.put("sum", GROUP_SUMMARY);
-        methodToGroup.put("all", GROUP_SUMMARY);
-        methodToGroup.put("any", GROUP_SUMMARY);
-        methodToGroup.put("range", GROUP_SUMMARY);
-    }
-
-    private static void addGroupComplex() {
-        methodToGroup.put("Arg", GROUP_COMPLEX);
-        methodToGroup.put("Conj", GROUP_COMPLEX);
-        methodToGroup.put("Im", GROUP_COMPLEX);
-        methodToGroup.put("Mod", GROUP_COMPLEX);
-        methodToGroup.put("Re", GROUP_COMPLEX);
-    }
-
 }
-- 
GitLab