From d0300fa2447281c73c71fcbb2baa40ed1eca6301 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Fri, 6 Oct 2017 11:35:28 +0200
Subject: [PATCH] Fixup .Devices variable in DevCurr built-in

---
 .../library/fastrGrid/grDevices/DevCurr.java  |  2 ++
 .../graphics/RGridGraphicsAdapter.java        | 32 ++++++++++++-----
 .../truffle/r/test/ExpectedTestOutput.test    | 10 ++++++
 .../r/test/builtins/TestBuiltin_devcur.java   | 36 +++++++++++++++++++
 4 files changed, 72 insertions(+), 8 deletions(-)
 create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_devcur.java

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCurr.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCurr.java
index b386e03956..924c7f411a 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCurr.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/DevCurr.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.library.fastrGrid.grDevices;
 
 import com.oracle.truffle.r.library.fastrGrid.GridContext;
+import com.oracle.truffle.r.library.fastrGrid.graphics.RGridGraphicsAdapter;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -34,6 +35,7 @@ public final class DevCurr extends RExternalBuiltinNode.Arg0 {
 
     @Override
     public RAbstractIntVector execute() {
+        RGridGraphicsAdapter.fixupDevicesVariable();
         int index = GridContext.getContext().getCurrentDeviceIndex();
         return RDataFactory.createIntVector(new int[]{index + 1}, RDataFactory.COMPLETE_VECTOR);
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java
index d0f5518a6e..651ec5ff1b 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java
@@ -14,6 +14,7 @@
  */
 package com.oracle.truffle.r.library.fastrGrid.graphics;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.library.fastrGrid.FastRGridExternalLookup;
 import com.oracle.truffle.r.runtime.FastRConfig;
 import com.oracle.truffle.r.runtime.RError;
@@ -79,9 +80,26 @@ public final class RGridGraphicsAdapter {
         }
     }
 
+    /**
+     * Fixup .Devices array as someone may have set it to something that is not a pair list nor
+     * RNull, which breaks dev.cur built-in R function and others. GNUR seems to have active binding
+     * for it. This is such special case that it doesn't seem necessary for now.
+     */
+    @TruffleBoundary
+    public static void fixupDevicesVariable() {
+        Object devices = REnvironment.baseEnv().get(DOT_DEVICES);
+        if (devices == RNull.instance || !(devices instanceof RPairList)) {
+            // reset the .Devices and .Device variables to initial values
+            REnvironment.baseEnv().safePut(DOT_DEVICES, RNull.instance);
+            addDevice(NULL_DEVICE);
+            setCurrentDevice(NULL_DEVICE);
+        }
+    }
+
     public static void removeDevice(int index) {
         assert index > 0 : "cannot remove null device";
         REnvironment baseEnv = REnvironment.baseEnv();
+        fixupDevicesVariable();
         RPairList devices = (RPairList) baseEnv.get(DOT_DEVICES);
         assert index < devices.getLength() : "wrong index in removeDevice";
         RPairList prev = devices;
@@ -95,7 +113,7 @@ public final class RGridGraphicsAdapter {
 
     public static void setCurrentDevice(String name) {
         REnvironment baseEnv = REnvironment.baseEnv();
-        assert contains((RPairList) baseEnv.get(DOT_DEVICES), name) : "setCurrentDevice can be invoked only after the device is added with addDevice";
+        assert contains(baseEnv.get(DOT_DEVICES), name) : "setCurrentDevice can be invoked only after the device is added with addDevice";
         baseEnv.safePut(DOT_DEVICE, name);
     }
 
@@ -124,13 +142,11 @@ public final class RGridGraphicsAdapter {
         return dotDevices instanceof RPairList ? ((RPairList) dotDevices).getLength() : 0;
     }
 
-    public static String getDeviceName(int index) {
-        RPairList dotDevices = (RPairList) REnvironment.baseEnv().get(DOT_DEVICES);
-        return RRuntime.asString(dotDevices.getDataAtAsObject(index));
-    }
-
-    private static boolean contains(RPairList devices, String name) {
-        for (RPairList dev : devices) {
+    private static boolean contains(Object devices, String name) {
+        if (!(devices instanceof RPairList)) {
+            return false;
+        }
+        for (RPairList dev : (RPairList) devices) {
             if (dev.car().equals(name)) {
                 return true;
             }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 85d7860d9a..05bc42255c 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -20393,6 +20393,16 @@ attr(,"class")
 #argv <- structure(list(x = structure(c(0, 0, 0, 0, 0, 0, NA,     0, 0, NA, NA, 0, 0, 0, 0, 1), .Dim = c(4L, 4L))), .Names = 'x');do.call('det', argv)
 [1] 0
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_devcur.simpleTests#
+#{ .Devices <- list(); dev.cur(); }
+null device
+          1
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_devcur.simpleTests#
+#{ dev.cur() }
+null device
+          1
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn1#
 #argv <- list('f is deprecated.\nUse convertY instead.\nSee help(Deprecated)', NULL); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
 NULL
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_devcur.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_devcur.java
new file mode 100644
index 0000000000..c3720ff4b5
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_devcur.java
@@ -0,0 +1,36 @@
+/*
+ * 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.test.builtins;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.test.TestBase;
+
+public class TestBuiltin_devcur extends TestBase {
+
+    @Test
+    public void simpleTests() {
+        assertEval("{ dev.cur() }");
+        assertEval("{ .Devices <- list(); dev.cur(); }");
+    }
+}
-- 
GitLab