From 4a81767a121bb7b76f1bc41343981150e9deeceb Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Fri, 21 Jul 2017 10:44:27 +0200
Subject: [PATCH] RFFI Fix: ATTRIB should return RList not DynamicObject

---
 .../ffi/impl/common/JavaUpCallsRFFIImpl.java  |   2 +-
 .../truffle/r/ffi/impl/nodes/ATTRIB.java      |  53 +++++++
 .../r/ffi/impl/nodes/FFIUpCallRootNode.java   |   1 +
 .../truffle/r/nodes/builtin/base/Attr.java    |  19 +--
 .../r/nodes/builtin/base/Attributes.java      |  77 +---------
 .../r/nodes/attributes/GetAttributesNode.java | 141 ++++++++++++++++++
 6 files changed, 204 insertions(+), 89 deletions(-)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java
 create mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributesNode.java

diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
index 7e0bd5d9b1..00481e0268 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
@@ -264,7 +264,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     @Override
     public Object ATTRIB(Object obj) {
         if (obj instanceof RAttributable) {
-            return ((RAttributable) obj).getAttributes();
+            return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.ATTRIB).call(obj);
         }
         return RNull.instance;
     }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java
new file mode 100644
index 0000000000..07d6a98681
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.nodes;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.GetAttributesNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public abstract class ATTRIB extends FFIUpCallNode.Arg1 {
+    @Child private GetAttributesNode getAttributesNode = GetAttributesNode.create();
+
+    @Specialization
+    public Object doAttributable(RAttributable obj) {
+        return getAttributesNode.execute(obj);
+    }
+
+    @Fallback
+    public RNull doOthers(Object obj) {
+        if (obj == RNull.instance || RRuntime.isForeignObject(obj)) {
+            return RNull.instance;
+        } else {
+            CompilerDirectives.transferToInterpreter();
+            String type = obj == null ? "null" : obj.getClass().getSimpleName();
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "object of type '" + type + "' cannot be attributed");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
index fe43cc4cc1..a3bd560c96 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
@@ -90,6 +90,7 @@ public final class FFIUpCallRootNode extends RootNode {
     }
 
     public static void register() {
+        FFIUpCallRootNode.add(RFFIUpCallTable.ATTRIB, ATTRIBNodeGen::create);
         FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asReal, AsRealNodeGen::create);
         FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asLogical, AsLogicalNodeGen::create);
         FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asInteger, AsIntegerNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
index 42325e9757..3e2489c23c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.GetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.GetAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.IterableAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetRowNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
@@ -47,11 +48,9 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
 @RBuiltin(name = "attr", kind = PRIMITIVE, parameterNames = {"x", "which", "exact"}, behavior = PURE)
 public abstract class Attr extends RBuiltinNode.Arg3 {
@@ -124,7 +123,7 @@ public abstract class Attr extends RBuiltinNode.Arg3 {
         if (attributes == null) {
             return RNull.instance;
         } else {
-            return getFullRowNames(getRowNamesNode.getRowNames(container));
+            return GetAttributesNode.getFullRowNames(getRowNamesNode.getRowNames(container));
         }
     }
 
@@ -143,20 +142,6 @@ public abstract class Attr extends RBuiltinNode.Arg3 {
         }
     }
 
-    public static Object getFullRowNames(Object a) {
-        if (a == RNull.instance) {
-            return RNull.instance;
-        } else {
-            if (a instanceof RAbstractIntVector) {
-                RAbstractIntVector rowNames = (RAbstractIntVector) a;
-                if (rowNames.getLength() == 2 && RRuntime.isNA(rowNames.getDataAt(0))) {
-                    return RDataFactory.createIntSequence(1, 1, Math.abs(rowNames.getDataAt(1)));
-                }
-            }
-            return a;
-        }
-    }
-
     protected static boolean isRowNamesAttr(String name) {
         return name.equals(RRuntime.ROWNAMES_ATTR_KEY);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
index 5f3aee4e78..668410e59d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
@@ -25,109 +25,44 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import java.util.Arrays;
-
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.object.DynamicObject;
-import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.r.nodes.attributes.ArrayAttributeNode;
-import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.GetAttributesNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.env.REnvironment;
 
 @RBuiltin(name = "attributes", kind = PRIMITIVE, parameterNames = {"obj"}, behavior = PURE)
 public abstract class Attributes extends RBuiltinNode.Arg1 {
 
-    private final BranchProfile rownamesBranch = BranchProfile.create();
-    @Child private ArrayAttributeNode arrayAttrAccess = ArrayAttributeNode.create();
-    @Child private SetNamesAttributeNode setNamesNode = SetNamesAttributeNode.create();
+    @Child private GetAttributesNode getAttributesNode = GetAttributesNode.create();
 
     static {
         Casts.noCasts(Attributes.class);
     }
 
     @Specialization
-    protected Object attributesNull(RAbstractContainer container,
-                    @Cached("createBinaryProfile()") ConditionProfile hasAttributesProfile) {
-        if (hasAttributesProfile.profile(hasAttributes(container))) {
-            return createResult(container, container instanceof RLanguage);
-        } else {
-            return RNull.instance;
-        }
+    protected Object attributesNull(RAbstractContainer container) {
+        return getAttributesNode.execute(container);
     }
 
     /**
-     * Unusual cases that it is not worth specializing on as they are not performance-centric,
-     * basically any type that is not an {@link RAbstractContainer} but is {@link RAttributable},
-     * e.g. {@link REnvironment}.
+     * Unusual cases that it is not worth specializing on as they are not performance-centric.
      */
     @Fallback
     @TruffleBoundary
     protected Object attributes(Object object) {
         if (object instanceof RAttributable) {
-            if (!hasAttributes((RAttributable) object)) {
-                return RNull.instance;
-            } else {
-                return createResult((RAttributable) object, false);
-            }
+            return getAttributesNode.execute((RAttributable) object);
         } else if (object == RNull.instance || RRuntime.isForeignObject(object)) {
             return RNull.instance;
         } else {
             throw RError.nyi(this, "object cannot be attributed");
         }
     }
-
-    /**
-     * {@code language} objects behave differently regarding "names"; they don't get included.
-     */
-    private Object createResult(RAttributable attributable, boolean ignoreNames) {
-        DynamicObject attributes = attributable.getAttributes();
-        int size = attributes.size();
-        String[] names = new String[size];
-        Object[] values = new Object[size];
-        int z = 0;
-        for (RAttributesLayout.RAttribute attr : arrayAttrAccess.execute(attributes)) {
-            String name = attr.getName();
-            if (ignoreNames && name.equals(RRuntime.NAMES_ATTR_KEY)) {
-                continue;
-            }
-            names[z] = name;
-            if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
-                rownamesBranch.enter();
-                values[z] = Attr.getFullRowNames(attr.getValue());
-            } else {
-                values[z] = attr.getValue();
-            }
-            z++;
-        }
-        if (ignoreNames && z != names.length) {
-            if (z == 0) {
-                return RNull.instance;
-            } else {
-                names = Arrays.copyOfRange(names, 0, names.length - 1);
-                values = Arrays.copyOfRange(values, 0, values.length - 1);
-            }
-        }
-        RList result = RDataFactory.createList(values);
-        setNamesNode.setNames(result, RDataFactory.createStringVector(names, true));
-        return result;
-    }
-
-    private static boolean hasAttributes(RAttributable attributable) {
-        return attributable.getAttributes() != null && !attributable.getAttributes().isEmpty();
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributesNode.java
new file mode 100644
index 0000000000..1b2ff7d4ac
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributesNode.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.nodes.attributes;
+
+import java.util.Arrays;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributesLayout;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+/**
+ * Transforms the attributes from {@link DynamicObject} into {@link RList}.
+ */
+public abstract class GetAttributesNode extends RBaseNode {
+
+    private final BranchProfile rownamesBranch = BranchProfile.create();
+    @Child private ArrayAttributeNode arrayAttrAccess = ArrayAttributeNode.create();
+    @Child private SetNamesAttributeNode setNamesNode = SetNamesAttributeNode.create();
+
+    public static GetAttributesNode create() {
+        return GetAttributesNodeGen.create();
+    }
+
+    public abstract Object execute(RAttributable attributable);
+
+    @Specialization
+    protected Object attributesNull(RAbstractContainer container,
+                    @Cached("createBinaryProfile()") ConditionProfile hasAttributesProfile) {
+        if (hasAttributesProfile.profile(hasAttributes(container))) {
+            return createResult(container, container instanceof RLanguage);
+        } else {
+            return RNull.instance;
+        }
+    }
+
+    /**
+     * Unusual cases that it is not worth specializing on as they are not performance-centric,
+     * basically any type that is not an {@link RAbstractContainer} but is {@link RAttributable},
+     * e.g. {@link REnvironment}.
+     */
+    @Fallback
+    @TruffleBoundary
+    protected Object attributes(RAttributable object) {
+        if (hasAttributes(object)) {
+            return createResult(object, false);
+        } else {
+            return RNull.instance;
+        }
+    }
+
+    public static Object getFullRowNames(Object a) {
+        if (a == RNull.instance) {
+            return RNull.instance;
+        } else {
+            if (a instanceof RAbstractIntVector) {
+                RAbstractIntVector rowNames = (RAbstractIntVector) a;
+                if (rowNames.getLength() == 2 && RRuntime.isNA(rowNames.getDataAt(0))) {
+                    return RDataFactory.createIntSequence(1, 1, Math.abs(rowNames.getDataAt(1)));
+                }
+            }
+            return a;
+        }
+    }
+
+    /**
+     * {@code language} objects behave differently regarding "names"; they don't get included.
+     */
+    private Object createResult(RAttributable attributable, boolean ignoreNames) {
+        DynamicObject attributes = attributable.getAttributes();
+        int size = attributes.size();
+        String[] names = new String[size];
+        Object[] values = new Object[size];
+        int z = 0;
+        for (RAttributesLayout.RAttribute attr : arrayAttrAccess.execute(attributes)) {
+            String name = attr.getName();
+            if (ignoreNames && name.equals(RRuntime.NAMES_ATTR_KEY)) {
+                continue;
+            }
+            names[z] = name;
+            if (name.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
+                rownamesBranch.enter();
+                values[z] = getFullRowNames(attr.getValue());
+            } else {
+                values[z] = attr.getValue();
+            }
+            z++;
+        }
+        if (ignoreNames && z != names.length) {
+            if (z == 0) {
+                return RNull.instance;
+            } else {
+                names = Arrays.copyOfRange(names, 0, names.length - 1);
+                values = Arrays.copyOfRange(values, 0, values.length - 1);
+            }
+        }
+        RList result = RDataFactory.createList(values);
+        setNamesNode.setNames(result, RDataFactory.createStringVector(names, true));
+        return result;
+    }
+
+    private static boolean hasAttributes(RAttributable attributable) {
+        return attributable.getAttributes() != null && !attributable.getAttributes().isEmpty();
+    }
+}
-- 
GitLab