From d9279cb54f457877deb57af3dad227a637c917c5 Mon Sep 17 00:00:00 2001
From: Adam Welc <adam.welc@oracle.com>
Date: Thu, 17 Dec 2015 17:33:25 -0800
Subject: [PATCH] Added support for checking if lists, S4 objects and external
 pointers are identical.

---
 .../r/nodes/builtin/base/BasePackage.java     |   2 +-
 .../r/nodes/builtin/base/Identical.java       | 144 +++++++++++++++---
 .../test/builtins/TestBuiltin_identical.java  |   8 +
 3 files changed, 135 insertions(+), 19 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index c64c7dc9de..0650d59a1b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -283,7 +283,7 @@ public class BasePackage extends RBuiltinPackage {
         add(HiddenInternalFunctions.RefCountInfo.class, HiddenInternalFunctionsFactory.RefCountInfoNodeGen::create);
         add(HiddenInternalFunctions.Identity.class, HiddenInternalFunctionsFactory.IdentityNodeGen::create);
         add(IConv.class, IConvNodeGen::create);
-        add(Identical.class, IdenticalNodeGen::create);
+        add(Identical.class, Identical::create);
         add(Im.class, ImNodeGen::create);
         add(InfixEmulationFunctions.AccessArraySubscriptBuiltin.class, InfixEmulationFunctionsFactory.AccessArraySubscriptBuiltinNodeGen::create);
         add(InfixEmulationFunctions.AccessArraySubscriptDefaultBuiltin.class, InfixEmulationFunctionsFactory.AccessArraySubscriptBuiltinNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
index 37c9a35b0c..78bdc41293 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
@@ -24,13 +24,18 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
+import java.util.Iterator;
+
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.builtin.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
+import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
+import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
@@ -46,34 +51,59 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 @RBuiltin(name = "identical", kind = INTERNAL, parameterNames = {"x", "y", "num.eq", "single.NA", "attrib.as.set", "ignore.bytecode", "ignore.environment"})
 public abstract class Identical extends RBuiltinNode {
 
+    protected abstract byte executeByte(Object x, Object y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment);
+
+    @Child private Identical identicalRecursive;
+    private final boolean recursive;
+
+    public Identical(boolean recursive) {
+        this.recursive = recursive;
+    }
+
     private final ConditionProfile vecLengthProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile naArgsProfile = ConditionProfile.createBinaryProfile();
 
+    private byte identicalRecursive(Object x, Object y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
+        if (identicalRecursive == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            identicalRecursive = insert(IdenticalNodeGen.create(true, new RNode[7], null, null));
+        }
+        return identicalRecursive.executeByte(x, y, numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
+    }
+
     @SuppressWarnings("unused")
     @Specialization(guards = "isRNull(x) || isRNull(y)")
     protected byte doInternalIdentical(Object x, Object y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return x == y ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(byte x, byte y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return x == y ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(String x, String y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return x.equals(y) ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(double x, double y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         boolean truth = numEq == RRuntime.LOGICAL_TRUE ? x == y : Double.doubleToRawLongBits(x) == Double.doubleToRawLongBits(y);
         return truth ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
@@ -81,21 +111,27 @@ public abstract class Identical extends RBuiltinNode {
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(RAbstractLogicalVector x, REnvironment y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(REnvironment x, RAbstractLogicalVector y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(REnvironment x, REnvironment y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         // reference equality for environments
         return x == y ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
@@ -103,13 +139,17 @@ public abstract class Identical extends RBuiltinNode {
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdentical(RSymbol x, RSymbol y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return x.getName().equals(y.getName()) ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE;
     }
 
     @Specialization
     protected byte doInternalIdentical(RLanguage x, RLanguage y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         if (naArgsProfile.profile(checkExtraArgsForNA(numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment))) {
             if (x == y) {
                 return RRuntime.LOGICAL_TRUE;
@@ -124,14 +164,18 @@ public abstract class Identical extends RBuiltinNode {
     @SuppressWarnings("unused")
     @Specialization
     byte doInternalIdentical(RFunction x, RFunction y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return RRuntime.asLogical(x == y);
     }
 
     @SuppressWarnings("unused")
     @Specialization(guards = "!vectorsLists(x, y)")
     protected byte doInternalIdenticalGeneric(RAbstractVector x, RAbstractVector y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         if (vecLengthProfile.profile(x.getLength() != y.getLength())) {
             return RRuntime.LOGICAL_FALSE;
         } else {
@@ -147,41 +191,101 @@ public abstract class Identical extends RBuiltinNode {
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdenticalGeneric(RList x, RList y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
-        throw RError.nyi(this, "lists not supported in 'identical'");
+        if (!recursive) {
+            controlVisibility();
+        }
+        if (x.getLength() != y.getLength()) {
+            return RRuntime.LOGICAL_FALSE;
+        }
+        for (int i = 0; i < x.getLength(); i++) {
+            byte res = identicalRecursive(x.getDataAt(i), y.getDataAt(i), numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
+            if (res == RRuntime.LOGICAL_FALSE) {
+                return RRuntime.LOGICAL_FALSE;
+            }
+        }
+        return RRuntime.LOGICAL_TRUE;
     }
 
     @Specialization
     protected byte doInternalIdenticalGeneric(RDataFrame x, RDataFrame y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return doInternalIdenticalGeneric(x.getVector(), y.getVector(), numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdenticalGeneric(RFunction x, RAbstractContainer y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdenticalGeneric(RLanguage x, RAbstractContainer y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return RRuntime.LOGICAL_FALSE;
     }
 
     @SuppressWarnings("unused")
     @Specialization
     protected byte doInternalIdenticalGeneric(RAbstractContainer x, RFunction y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         return RRuntime.LOGICAL_FALSE;
     }
 
+    @SuppressWarnings("unused")
+    @Specialization
+    protected byte doInternalIdenticalGeneric(RS4Object x, RS4Object y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
+        if (!recursive) {
+            controlVisibility();
+        }
+        if (x.isS4() != y.isS4()) {
+            return RRuntime.LOGICAL_FALSE;
+        }
+        RAttributes xAttributes = x.getAttributes();
+        RAttributes yAttributes = y.getAttributes();
+        if (xAttributes.size() == yAttributes.size()) {
+            Iterator<RAttribute> xIter = xAttributes.iterator();
+            Iterator<RAttribute> yIter = yAttributes.iterator();
+            while (xIter.hasNext()) {
+                RAttribute xAttr = xIter.next();
+                RAttribute yAttr = yIter.next();
+                if (!xAttr.getName().equals(yAttr.getName())) {
+                    return RRuntime.LOGICAL_FALSE;
+                }
+                byte res = identicalRecursive(xAttr.getValue(), yAttr.getValue(), numEq, singleNA, attribAsSet, ignoreBytecode, ignoreEnvironment);
+                if (res == RRuntime.LOGICAL_FALSE) {
+                    return RRuntime.LOGICAL_FALSE;
+                }
+            }
+            return RRuntime.LOGICAL_TRUE;
+        }
+        return RRuntime.LOGICAL_FALSE;
+    }
+
+    @SuppressWarnings("unused")
+    @Specialization
+    protected byte doInternalIdenticalGeneric(RExternalPtr x, RExternalPtr y, Object numEq, Object singleNA, Object attribAsSet, Object ignoreBytecode, Object ignoreEnvironment) {
+        if (!recursive) {
+            controlVisibility();
+        }
+        return RRuntime.asLogical(x.getAddr() == y.getAddr());
+    }
+
     @SuppressWarnings("unused")
     @Fallback
     protected byte doInternalIdenticalWrongTypes(Object x, Object y, byte numEq, byte singleNA, byte attribAsSet, byte ignoreBytecode, byte ignoreEnvironment) {
-        controlVisibility();
+        if (!recursive) {
+            controlVisibility();
+        }
         if (x.getClass() != y.getClass()) {
             return RRuntime.LOGICAL_FALSE;
         } else {
@@ -205,4 +309,8 @@ public abstract class Identical extends RBuiltinNode {
         return true;
     }
 
+    public static Identical create(RNode[] arguments, RBuiltinFactory builtin, ArgumentsSignature suppliedSignature) {
+        return IdenticalNodeGen.create(false, arguments, builtin, suppliedSignature);
+    }
+
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_identical.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_identical.java
index 0a1ea03709..654e098a51 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_identical.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_identical.java
@@ -222,5 +222,13 @@ public class TestBuiltin_identical extends TestBase {
         assertEval("{ identical(quote(if(x) 42), quote(if(x) 7)) }");
         assertEval("{ identical(quote(if(x) 42), quote(if(x) 42)) }");
         assertEval("{ identical(function() 42, function() 42) }");
+
+        assertEval("{ setClass(\"foo\", representation(j=\"numeric\")); x<-new(\"foo\", j=42); y<-new(\"foo\", j=42); identical(x,y) }");
+        assertEval("{ setClass(\"foo\", representation(j=\"numeric\")); x<-new(\"foo\", j=42); y<-new(\"foo\", j=7); identical(x,y) }");
+
+        assertEval("{ x<-list(7); y<-list(7); identical(x,y) }");
+        assertEval("{ x<-list(7); y<-list(42); identical(x,y) }");
+        assertEval("{ x<-list(list(7)); y<-list(list(7)); identical(x,y) }");
+        assertEval("{ x<-list(list(7)); y<-list(list(42)); identical(x,y) }");
     }
 }
-- 
GitLab