From dd9691ddb81998fcffd324f895483e4327dad4a5 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Thu, 23 Mar 2017 17:13:27 +0100
Subject: [PATCH] FastR Grid: support gpar containing vectors

---
 .../r/library/fastrGrid/DoSetViewPort.java    | 40 ++++----
 .../r/library/fastrGrid/DrawArrowsNode.java   |  2 +-
 .../truffle/r/library/fastrGrid/GPar.java     | 91 ++++++++++++++-----
 .../r/library/fastrGrid/GridLinesNode.java    |  8 +-
 .../r/library/fastrGrid/GridState.java        |  9 +-
 .../r/library/fastrGrid/GridTextNode.java     |  8 +-
 .../r/library/fastrGrid/GridUtils.java        | 13 +++
 .../truffle/r/library/fastrGrid/LCircle.java  |  7 +-
 .../truffle/r/library/fastrGrid/LConvert.java |  7 +-
 .../library/fastrGrid/LInitViewPortStack.java |  1 -
 .../truffle/r/library/fastrGrid/LPoints.java  | 19 +++-
 .../truffle/r/library/fastrGrid/LRect.java    |  7 +-
 .../r/library/fastrGrid/LSegments.java        |  7 +-
 .../truffle/r/library/fastrGrid/Unit.java     | 26 +++---
 .../device/BufferedJFrameDevice.java          |  2 +-
 .../r/library/fastrGrid/device/GridColor.java |  5 +-
 16 files changed, 155 insertions(+), 97 deletions(-)

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java
index 591c4e25f4..3154017025 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.r.library.fastrGrid.Unit.IsRelativeUnitNode;
 import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
 import com.oracle.truffle.r.library.fastrGrid.ViewPort.LayoutPos;
 import com.oracle.truffle.r.library.fastrGrid.ViewPort.LayoutSize;
-import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.RError.Message;
@@ -65,11 +64,11 @@ class DoSetViewPort extends RBaseNode {
         }
 
         GridDevice currentDevice = GridContext.getContext().getCurrentDevice();
-        DrawingContext deviceDrawingContext = GridState.getInitialGPar(currentDevice);
+        GPar gpar = GridState.getInitialGPar(currentDevice);
 
         RList parent = asListOrNull(pushedViewPort.getDataAt(ViewPort.PVP_PARENT));
         boolean doNotRecalculateParent = hasParent && !ViewPort.updateDeviceSizeInVP(parent, currentDevice);
-        calcViewportTransform(pushedViewPort, parent, doNotRecalculateParent, currentDevice, deviceDrawingContext);
+        calcViewportTransform(pushedViewPort, parent, doNotRecalculateParent, currentDevice, gpar);
 
         // TODO: clipping
         pushedVPData[ViewPort.PVP_CLIPRECT] = RDataFactory.createDoubleVector(new double[]{0, 0, 0, 0}, RDataFactory.COMPLETE_VECTOR);
@@ -87,37 +86,37 @@ class DoSetViewPort extends RBaseNode {
      * @param incremental If {@code true} it is assumed that we can just take the transformation
      *            matrix and other values from the parent without re-calculating them recursively.
      * @param device This method needs the device in order to convert units
-     * @param deviceDrawingContext This method needs to know the device default drawing context in
+     * @param deviceTopLevelGpar This method needs to know the device default drawing context in
      *            order to convert units for the top level view port
      */
     @TruffleBoundary
-    public void calcViewportTransform(RList viewPort, Object parent, boolean incremental, GridDevice device, DrawingContext deviceDrawingContext) {
+    public void calcViewportTransform(RList viewPort, Object parent, boolean incremental, GridDevice device, GPar deviceTopLevelGpar) {
         double[][] parentTransform;
         ViewPortContext parentContext;
         ViewPortLocation vpl;
         Size parentSize;
-        DrawingContext drawingContext;
+        GPar drawingContext;
         double parentAngle;
         if (parent == null || parent == RNull.instance) {
             parentTransform = TransformMatrix.identity();
             parentContext = ViewPortContext.createDefault();
             parentSize = new Size(device.getWidth(), device.getHeight());
             vpl = ViewPortLocation.fromViewPort(viewPort);
-            drawingContext = deviceDrawingContext;
+            drawingContext = deviceTopLevelGpar;
             parentAngle = 0;
         } else {
             assert parent instanceof RList : "inconsistent data: parent of a viewport must be a list";
             RList parentVPList = (RList) parent;
             Object[] parentData = parentVPList.getDataWithoutCopying();
             if (!incremental) {
-                calcViewportTransform(parentVPList, parentData[ViewPort.PVP_PARENT], false, device, deviceDrawingContext);
+                calcViewportTransform(parentVPList, parentData[ViewPort.PVP_PARENT], false, device, deviceTopLevelGpar);
             }
             parentSize = new Size(Unit.cmToInches(castScalar(parentData[ViewPort.PVP_WIDTHCM])), Unit.cmToInches(castScalar(parentData[ViewPort.PVP_HEIGHTCM])));
             parentTransform = fromFlat(castDoubleVector(parentData[ViewPort.PVP_TRANS]).materialize().getDataWithoutCopying());
             parentContext = ViewPortContext.fromViewPort(parentVPList);
             parentAngle = asDouble(parentData[ViewPort.PVP_ROTATION]);
 
-            drawingContext = GPar.asDrawingContext(asList(viewPort.getDataAt(ViewPort.PVP_PARENTGPAR)));
+            drawingContext = GPar.create(asList(viewPort.getDataAt(ViewPort.PVP_PARENTGPAR)));
             boolean noLayout = (isNull(viewPort.getDataAt(ViewPort.VP_VALIDLPOSROW)) && isNull(viewPort.getDataAt(ViewPort.VP_VALIDLPOSCOL))) || isNull(parentData[ViewPort.VP_LAYOUT]);
             if (noLayout) {
                 vpl = ViewPortLocation.fromViewPort(viewPort);
@@ -157,8 +156,7 @@ class DoSetViewPort extends RBaseNode {
         // Finally, allocate the rows and columns for this viewport's layout if it has one
         if (!isNull(viewPort.getDataAt(ViewPort.VP_LAYOUT))) {
             ViewPortContext vpCtx = ViewPortContext.fromViewPort(viewPort);
-            DrawingContext drawingCtx = GPar.asDrawingContext(asList(viewPort.getDataAt(ViewPort.PVP_GPAR)));
-            calcViewPortLayout(viewPort, new Size(width, height), vpCtx, device, drawingCtx);
+            calcViewPortLayout(viewPort, new Size(width, height), vpCtx, device, GPar.create(asList(viewPort.getDataAt(ViewPort.PVP_GPAR))));
         }
 
         Object[] viewPortData = viewPort.getDataWithoutCopying();
@@ -168,13 +166,13 @@ class DoSetViewPort extends RBaseNode {
         viewPortData[ViewPort.PVP_TRANS] = RDataFactory.createDoubleVector(flatten(transform), RDataFactory.COMPLETE_VECTOR, new int[]{3, 3});
     }
 
-    private void calcViewPortLayout(RList viewPort, Size size, ViewPortContext parentVPCtx, GridDevice device, DrawingContext drawingCtx) {
+    private void calcViewPortLayout(RList viewPort, Size size, ViewPortContext parentVPCtx, GridDevice device, GPar gpar) {
         LayoutSize layoutSize = LayoutSize.fromViewPort(viewPort);
         double[] npcWidths = new double[layoutSize.ncol];
         double[] npcHeights = new double[layoutSize.nrow];
         boolean[] relativeWidths = new boolean[layoutSize.ncol];
         boolean[] relativeHeights = new boolean[layoutSize.nrow];
-        UnitConversionContext conversionCtx = new UnitConversionContext(size, parentVPCtx, device, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(size, parentVPCtx, device, gpar);
 
         // For both dimensions we find out which units are other than "null" for those we can
         // immediately calculate the physical size in npcWidth/npcHeights. The reducedWidth/Height
@@ -195,8 +193,8 @@ class DoSetViewPort extends RBaseNode {
         int respect = RRuntime.asInteger(layoutAsList.getDataAt(ViewPort.LAYOUT_VRESPECT));
         int[] layoutRespectMat = ((RAbstractIntVector) layoutAsList.getDataAt(ViewPort.LAYOUT_MRESPECT)).materialize().getDataWithoutCopying();
         if ((reducedHeight > 0 || reducedWidth > 0) && respect > 0) {
-            double sumRelWidth = sumRelativeDimension(layoutWidths, relativeWidths, parentVPCtx, device, drawingCtx, true);
-            double sumRelHeight = sumRelativeDimension(layoutHeights, relativeHeights, parentVPCtx, device, drawingCtx, false);
+            double sumRelWidth = sumRelativeDimension(layoutWidths, relativeWidths, parentVPCtx, device, gpar, true);
+            double sumRelHeight = sumRelativeDimension(layoutHeights, relativeHeights, parentVPCtx, device, gpar, false);
             double tempWidth = reducedWidth;
             double tempHeight = reducedHeight;
             double denom;
@@ -250,8 +248,8 @@ class DoSetViewPort extends RBaseNode {
         }
 
         // Secondly, allocate remaining relative widths and heights in the remaining space
-        allocateRelativeDim(layoutSize, layoutWidths, npcWidths, relativeWidths, reducedWidth, respect, layoutRespectMat, device, drawingCtx, parentVPCtx, true);
-        allocateRelativeDim(layoutSize, layoutHeights, npcHeights, relativeHeights, reducedHeight, respect, layoutRespectMat, device, drawingCtx, parentVPCtx, false);
+        allocateRelativeDim(layoutSize, layoutWidths, npcWidths, relativeWidths, reducedWidth, respect, layoutRespectMat, device, gpar, parentVPCtx, true);
+        allocateRelativeDim(layoutSize, layoutHeights, npcHeights, relativeHeights, reducedHeight, respect, layoutRespectMat, device, gpar, parentVPCtx, false);
 
         // Create the result
         Object[] vpData = viewPort.getDataWithoutCopying();
@@ -260,9 +258,9 @@ class DoSetViewPort extends RBaseNode {
     }
 
     private void allocateRelativeDim(LayoutSize layoutSize, RAbstractContainer layoutItems, double[] npcItems, boolean[] relativeItems, double reducedDim, int respect, int[] layoutRespectMat,
-                    GridDevice device, DrawingContext drawingCtx, ViewPortContext parentVPCtx, boolean isWidth) {
+                    GridDevice device, GPar gpar, ViewPortContext parentVPCtx, boolean isWidth) {
         assert relativeItems.length == npcItems.length;
-        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, drawingCtx, 1, 0);
+        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, gpar, 1, 0);
         double totalUnrespectedSize = 0;
         if (reducedDim > 0) {
             for (int i = 0; i < relativeItems.length; i++) {
@@ -311,9 +309,9 @@ class DoSetViewPort extends RBaseNode {
         return false;
     }
 
-    private double sumRelativeDimension(RAbstractContainer layoutItems, boolean[] relativeItems, ViewPortContext parentVPCtx, GridDevice device, DrawingContext drawingCtx,
+    private double sumRelativeDimension(RAbstractContainer layoutItems, boolean[] relativeItems, ViewPortContext parentVPCtx, GridDevice device, GPar gpar,
                     boolean isWidth) {
-        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, drawingCtx, 1, 0);
+        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, gpar, 1, 0);
         double totalWidth = 0;
         for (int i = 0; i < relativeItems.length; i++) {
             if (relativeItems[i]) {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java
index e177c722b1..cda58b06a3 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java
@@ -66,7 +66,7 @@ class DrawArrowsNode extends Node {
         arrowLength = Math.max(arrowLength, unitToInches.convertWidth(lengthVec, parentIndex, conversionCtx));
         // draw the arrows
         GridDevice device = conversionCtx.device;
-        DrawingContext drawingCtx = conversionCtx.drawingContext;
+        DrawingContext drawingCtx = conversionCtx.gpar.getDrawingContext(parentIndex);
         if (first && start) {
             drawArrow(drawingCtx, device, arrowType, x[startIndex], y[startIndex], x[startIndex + 1], y[startIndex + 1], angle, arrowLength);
         }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
index be7d76d5f9..16288015d9 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
@@ -13,6 +13,7 @@ package com.oracle.truffle.r.library.fastrGrid;
 
 import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer;
 import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
 
 import java.util.Arrays;
 
@@ -30,10 +31,20 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
  * In the context of grid package, GPar is a list that contains the parameters for the drawing, like
- * line style, color, etc. This class wraps the list and provides type-safe access to its elements.
+ * line style, color, etc. This class wraps the list and provides way to convert it to
+ * {@link DrawingContext}. First create instance of {@link GPar} and then use
+ * {@link #getDrawingContext(int)} to get the drawing context. Note that grid's gpar can
+ * theoretically contain vector as the value of some graphical parameters, in such case, when
+ * drawing i-th element, e.g. i-th rectangle in {@link LRect}, we should use i-th (mod length)
+ * element of such vector. Note that this is sort of ignored in layout calculations, where we always
+ * take the first element. In other words, instance of {@link GPar} represents grid's gpar where the
+ * graphical parameter may be vectors, whereas {@link DrawingContext} is flattened view where it is
+ * already determined which index is used to access the vectors.
  */
 public final class GPar {
     private static final int GP_FILL = 0;
@@ -86,6 +97,40 @@ public final class GPar {
                     "fontface"
     };
     private static final RStringVector NAMES_VECTOR = (RStringVector) RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
+    private final RList gpar;
+    // majority of gpar instances contains only scalar values, for those we make sure we do not
+    // create a new drawing context instance for every index.
+    private final boolean singleDrawingCtx;
+    private DrawingContext indexZeroDrawingCtx;
+
+    public GPar(RList gpar, boolean singleDrawingCtx) {
+        this.gpar = gpar;
+        this.gpar.makeSharedPermanent();
+        this.singleDrawingCtx = singleDrawingCtx;
+        indexZeroDrawingCtx = new GParDrawingContext(gpar, 0);
+    }
+
+    public static double getCex(RList gpar) {
+        return asDouble(gpar.getDataAt(GP_CEX));
+    }
+
+    public static GPar create(RList gpar) {
+        boolean singleDrawingCtx = true;
+        for (int i = 0; i < gpar.getLength(); i++) {
+            Object item = gpar.getDataAt(i);
+            if (item instanceof RAbstractVector) {
+                singleDrawingCtx &= ((RAbstractVector) item).getLength() == 1;
+            }
+        }
+        return new GPar(gpar, singleDrawingCtx);
+    }
+
+    public DrawingContext getDrawingContext(int cyclicIndex) {
+        if (singleDrawingCtx || cyclicIndex == 0) {
+            return indexZeroDrawingCtx;
+        }
+        return new GParDrawingContext(gpar, cyclicIndex);
+    }
 
     public static RList createNew(GridDevice device) {
         Object[] data = new Object[GP_LENGTH];
@@ -111,24 +156,17 @@ public final class GPar {
         return result;
     }
 
-    public static double getCex(RList gpar) {
-        return asDouble(gpar.getDataAt(GP_CEX));
-    }
-
-    public static DrawingContext asDrawingContext(RList gpar) {
-        return new GParDrawingContext(gpar);
-    }
-
     private static RAbstractDoubleVector newDoubleVec(double val) {
         return RDataFactory.createDoubleVectorFromScalar(val);
     }
 
     private static final class GParDrawingContext implements DrawingContext {
         private final Object[] data;
+        private final int index;
 
-        private GParDrawingContext(RList list) {
+        private GParDrawingContext(RList list, int index) {
             data = list.getDataWithoutCopying();
-            list.makeSharedPermanent();
+            this.index = index;
         }
 
         @Override
@@ -137,24 +175,27 @@ public final class GPar {
             if (lty == null || lty == RNull.instance) {
                 return DrawingContext.GRID_LINE_SOLID;
             }
-            String name = RRuntime.asString(lty);
-            if (name != null) {
-                return lineTypeFromName(name);
+            // convert string values
+            if (lty instanceof String || lty instanceof RAbstractStringVector) {
+                String name = GridUtils.asString(lty, index);
+                if (name != null) {
+                    return lineTypeFromName(name);
+                }
             }
+            // convert numeric values
             RAbstractContainer ltyVec = asAbstractContainer(lty);
-            int num;
+            int num; // NA will indicate error
             if (ltyVec.getLength() == 0) {
                 num = RRuntime.INT_NA;
             } else if (ltyVec instanceof RAbstractDoubleVector) {
-                double realVal = ((RAbstractDoubleVector) ltyVec).getDataAt(0);
+                double realVal = getDataAtMod((RAbstractDoubleVector) ltyVec, index);
                 num = RRuntime.isNA(realVal) ? RRuntime.INT_NA : (int) realVal;
             } else if (ltyVec instanceof RAbstractIntVector) {
-                num = ((RAbstractIntVector) ltyVec).getDataAt(0);
+                num = getDataAtMod((RAbstractIntVector) ltyVec, index);
             } else {
                 num = RRuntime.INT_NA;
             }
-
-            if (RRuntime.isNA(num) || num < LINE_STYLES.length) {
+            if (RRuntime.isNA(num) || num < 0 || num >= LINE_STYLES.length) {
                 throw RError.error(RError.NO_CALLER, Message.GENERIC, "Invalid line type.");
             }
             return LINE_STYLES[num];
@@ -167,22 +208,22 @@ public final class GPar {
 
         @Override
         public double getFontSize() {
-            return asDouble(data[GP_FONTSIZE]) * asDouble(data[GP_CEX]);
+            return asDouble(data[GP_FONTSIZE], index) * asDouble(data[GP_CEX], index);
         }
 
         @Override
         public GridFontStyle getFontStyle() {
-            return GridFontStyle.fromInt(RRuntime.asInteger(data[GP_FONT]));
+            return GridFontStyle.fromInt(GridUtils.asInt(data[GP_FONT], index));
         }
 
         @Override
         public String getFontFamily() {
-            return RRuntime.asString(data[GP_FONTFAMILY]);
+            return GridUtils.asString(data[GP_FONTFAMILY], index);
         }
 
         @Override
         public double getLineHeight() {
-            return asDouble(data[GP_LINEHEIGHT]);
+            return asDouble(data[GP_LINEHEIGHT], index);
         }
 
         @Override
@@ -190,8 +231,8 @@ public final class GPar {
             return getGridColor(GP_FILL);
         }
 
-        private GridColor getGridColor(int index) {
-            return GridColorUtils.gridColorFromString(RRuntime.asString(data[index]));
+        private GridColor getGridColor(int listIndex) {
+            return GridColorUtils.gridColorFromString(GridUtils.asString(data[listIndex], index));
         }
 
         private static final byte[] DASHED_LINE = new byte[]{4, 4};
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java
index 2cceaeb82a..d2bc1d2830 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java
@@ -51,10 +51,10 @@ public abstract class GridLinesNode extends Node {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         // Convert the list of vectors of indexes to type-safe array and calculate the max length of
         // the vectors.
@@ -67,7 +67,9 @@ public abstract class GridLinesNode extends Node {
 
         double[] xx = new double[maxIndexesLen + 1];    // plus one for polygons
         double[] yy = new double[maxIndexesLen + 1];
-        for (RAbstractIntVector unitIndexes : unitIndexesList) {
+        for (int unitIndexesListIdx = 0; unitIndexesListIdx < unitIndexesList.length; unitIndexesListIdx++) {
+            RAbstractIntVector unitIndexes = unitIndexesList[unitIndexesListIdx];
+            DrawingContext drawingCtx = gpar.getDrawingContext(unitIndexesListIdx);
             boolean oldIsFinite = false;
             int start = 0;
             int unitIndexesLen = unitIndexes.getLength();
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
index fbedb1f853..6774a7b722 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
@@ -11,7 +11,6 @@
  */
 package com.oracle.truffle.r.library.fastrGrid;
 
-import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -53,8 +52,12 @@ public final class GridState {
         gpar = GPar.createNew(currentDevice);
     }
 
-    public static DrawingContext getInitialGPar(GridDevice device) {
-        return GPar.asDrawingContext(GPar.createNew(device));
+    /**
+     * Returns something like a canonical gpar, or top level gpar. This is used when we need a
+     * context to do e.g. unit conversion, but we are in a situation that no context is available.
+     */
+    public static GPar getInitialGPar(GridDevice device) {
+        return GPar.create(GPar.createNew(device));
     }
 
     public RList getGpar() {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java
index b9d862b0fb..594bca1676 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java
@@ -95,10 +95,10 @@ public final class GridTextNode extends RBaseNode {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         int length = GridUtils.maxLength(unitLength, x, y);
 
@@ -131,7 +131,7 @@ public final class GridTextNode extends RBaseNode {
             boolean doDraw = true;
             Rectangle trect = null;
             if (checkOverlap || !draw) {
-                trect = textRect(loc, hjust, vjust, rotation, text, drawingCtx, dev);
+                trect = textRect(loc, hjust, vjust, rotation, text, gpar.getDrawingContext(i), dev);
                 for (int j = 0; j < boundsCount; j++) {
                     if (trect.intersects(bounds[j])) {
                         doDraw = false;
@@ -145,7 +145,7 @@ public final class GridTextNode extends RBaseNode {
 
             // actual drawing
             if (draw && doDraw) {
-                text(loc.x, loc.y, text, hjust, vjust, rotation, drawingCtx, dev);
+                text(loc.x, loc.y, text, hjust, vjust, rotation, gpar.getDrawingContext(i), dev);
             }
 
             // or bounds checking
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java
index b57eaaa994..8b34ba02b1 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java
@@ -26,6 +26,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 final class GridUtils {
@@ -161,6 +162,18 @@ final class GridUtils {
         throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non integer value " + val.getClass().getSimpleName());
     }
 
+    static String asString(Object val, int cyclicIndex) {
+        if (val instanceof String) {
+            return (String) val;
+        } else if (val instanceof RAbstractStringVector) {
+            RAbstractStringVector vec = (RAbstractStringVector) val;
+            if (vec.getLength() > 0) {
+                return vec.getDataAt(cyclicIndex % vec.getLength());
+            }
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non character value " + val.getClass().getSimpleName());
+    }
+
     static RAbstractIntVector asIntVector(Object value) {
         if (value instanceof Integer) {
             return RDataFactory.createIntVectorFromScalar((Integer) value);
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
index 4d0183bf8f..07ff0d4e40 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
@@ -17,7 +17,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
-import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -48,10 +47,10 @@ public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         int length = GridUtils.maxLength(unitLength, xVec, yVec, radiusVec);
         for (int i = 0; i < length; i++) {
@@ -59,7 +58,7 @@ public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
             double radius = RMath.fmin2(radiusSizes.getWidth(), radiusSizes.getHeight());
             Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
             Point loc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
-            dev.drawCircle(drawingCtx, loc.x, loc.y, radius);
+            dev.drawCircle(gpar.getDrawingContext(i), loc.x, loc.y, radius);
         }
         return RNull.instance;
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
index b2300de6ab..134def4c4e 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
@@ -23,7 +23,6 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.library.fastrGrid.Unit.AxisOrDimension;
 import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
-import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -57,10 +56,10 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         int length = unitLength.execute(units);
         double[] result = new double[length];
@@ -94,7 +93,7 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
                 boolean isX = axisTo.isHorizontal();
                 double scalemin = isX ? vpContext.xscalemin : vpContext.yscalemin;
                 double scalemax = isX ? vpContext.xscalemax : vpContext.yscalemax;
-                result[i] = Unit.convertFromInches(inches, unitTo, vpToSize, scalemin, scalemax, axisTo.isDimension(), drawingCtx);
+                result[i] = Unit.convertFromInches(inches, unitTo, vpToSize, scalemin, scalemax, axisTo.isDimension(), gpar.getDrawingContext(i));
             }
         }
 
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java
index 82c23f06ae..8a28365fc7 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java
@@ -11,7 +11,6 @@
  */
 package com.oracle.truffle.r.library.fastrGrid;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.library.fastrGrid.ViewPort.InitViewPortNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
index 07545f1ba2..d4bd5f8217 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
@@ -64,20 +64,22 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        RList gpar = ctx.getGridState().getGpar();
-        DrawingContext drawingCtx = GPar.asDrawingContext(gpar);
-        double cex = GPar.getCex(gpar);
+        RList gparList = ctx.getGridState().getGpar();
+        GPar gpar = GPar.create(gparList);
+        double cex = GPar.getCex(gparList);
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         // Note: unlike in other drawing primitives, we only consider length of x
         int length = unitLength.execute(xVec);
-        PointDrawingContext pointDrawingCtx = new PointDrawingContext(drawingCtx, drawingCtx.getFillColor(), drawingCtx.getFillColor());
+        DrawingContext initialDrawingCtx = gpar.getDrawingContext(0);
+        PointDrawingContext pointDrawingCtx = new PointDrawingContext(initialDrawingCtx, initialDrawingCtx.getFillColor(), initialDrawingCtx.getFillColor());
         for (int i = 0; i < length; i++) {
             Point loc = TransformMatrix.transLocation(Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx), vpTransform.transform);
             double size = unitToInches.convertWidth(sizeVec, i, conversionCtx);
             if (loc.isFinite() && Double.isFinite(size)) {
+                pointDrawingCtx = pointDrawingCtx.update(gpar.getDrawingContext(i));
                 pointDrawingCtx = drawSymbol(pointDrawingCtx, dev, cex, pchVec.getDataAt(i % pchVec.getLength()), size, loc.x, loc.y);
             }
         }
@@ -153,6 +155,13 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
             return new PointDrawingContext(inner, color, fillColor);
         }
 
+        private PointDrawingContext update(DrawingContext inner) {
+            if (this.inner == inner) {
+                return this;
+            }
+            return new PointDrawingContext(inner, this.color, this.fillColor);
+        }
+
         @Override
         public byte[] getLineType() {
             return inner.getLineType();
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
index a666b07ede..7c69b09329 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
@@ -19,7 +19,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
-import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -53,10 +52,10 @@ public abstract class LRect extends RExternalBuiltinNode.Arg6 {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         int length = GridUtils.maxLength(unitLength, xVec, yVec, wVec, hVec);
         for (int i = 0; i < length; i++) {
@@ -66,7 +65,7 @@ public abstract class LRect extends RExternalBuiltinNode.Arg6 {
             Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
             Point transLoc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
             Point loc = transLoc.justify(size, getDataAtMod(hjust, i), getDataAtMod(vjust, i));
-            dev.drawRect(drawingCtx, loc.x, loc.y, size.getWidth(), size.getHeight());
+            dev.drawRect(gpar.getDrawingContext(i), loc.x, loc.y, size.getWidth(), size.getHeight());
         }
         return RNull.instance;
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java
index 1a3dda410e..c2e1cf10ba 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java
@@ -17,7 +17,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
-import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
 import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -59,10 +58,10 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 {
         GridDevice dev = ctx.getCurrentDevice();
 
         RList currentVP = ctx.getGridState().getViewPort();
-        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
         ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
 
         int length = GridUtils.maxLength(unitLength, x0, y0, x1, y1);
         double[] xx = new double[2];
@@ -77,7 +76,7 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 {
             xx[1] = loc2.x;
             yy[0] = loc1.y;
             yy[1] = loc2.y;
-            dev.drawPolyLines(drawingCtx, xx, yy, 0, 2);
+            dev.drawPolyLines(gpar.getDrawingContext(i), xx, yy, 0, 2);
             if (arrow != null) {
                 drawArrowsNode.drawArrows(xx, yy, 0, 2, i, arrow, true, true, conversionCtx);
             }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
index 4cb343dfbf..3c20956103 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
@@ -183,7 +183,7 @@ public class Unit {
         }
     }
 
-    private static double convertToInches(double value, int unitId, RList data, UnitConversionContext ctx, AxisOrDimension axisOrDim) {
+    private static double convertToInches(double value, int index, int unitId, RList data, UnitConversionContext ctx, AxisOrDimension axisOrDim) {
         double vpSize = ctx.getViewPortSize(axisOrDim);
         switch (unitId) {
             case INCHES:
@@ -201,16 +201,16 @@ public class Unit {
                 return value / (CM_IN_INCH * 10);
             case CHAR:
             case MYCHAR:
-                return (value * ctx.drawingContext.getFontSize()) / INCH_TO_POINTS_FACTOR;
+                return (value * ctx.gpar.getDrawingContext(index).getFontSize()) / INCH_TO_POINTS_FACTOR;
             case LINES:
             case MYLINES:
-                return (value * ctx.drawingContext.getFontSize() * ctx.drawingContext.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+                return (value * ctx.gpar.getDrawingContext(index).getFontSize() * ctx.gpar.getDrawingContext(index).getLineHeight()) / INCH_TO_POINTS_FACTOR;
             case STRINGWIDTH:
             case MYSTRINGWIDTH:
-                return ctx.device.getStringWidth(ctx.drawingContext, RRuntime.asString(data.getDataAt(0)));
+                return ctx.device.getStringWidth(ctx.gpar.getDrawingContext(index), RRuntime.asString(data.getDataAt(0)));
             case STRINGHEIGHT:
             case MYSTRINGHEIGHT:
-                return ctx.device.getStringHeight(ctx.drawingContext, RRuntime.asString(data.getDataAt(0)));
+                return ctx.device.getStringHeight(ctx.gpar.getDrawingContext(index), RRuntime.asString(data.getDataAt(0)));
             case NULL:
                 return evaluateNullUnit(value, vpSize, ctx.nullLayoutMode, ctx.nullArithmeticMode);
             default:
@@ -423,20 +423,20 @@ public class Unit {
     public static final class UnitConversionContext {
         public final Size viewPortSize;
         public final ViewPortContext viewPortContext;
-        public final DrawingContext drawingContext;
+        public final GPar gpar;
         public final GridDevice device;
         public final int nullLayoutMode;
         public final int nullArithmeticMode;
 
-        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, DrawingContext drawingContext) {
-            this(viewPortSize, viewPortContext, device, drawingContext, 0, 0);
+        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, GPar gpar) {
+            this(viewPortSize, viewPortContext, device, gpar, 0, 0);
         }
 
-        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, DrawingContext drawingContext, int nullLMode, int nullAMode) {
+        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, GPar gpar, int nullLMode, int nullAMode) {
             this.viewPortSize = viewPortSize;
             this.viewPortContext = viewPortContext;
             this.device = device;
-            this.drawingContext = drawingContext;
+            this.gpar = gpar;
             this.nullLayoutMode = nullLMode;
             this.nullArithmeticMode = nullAMode;
         }
@@ -495,7 +495,7 @@ public class Unit {
                 RList grobList = asList(value.getAttr(UNIT_ATTR_DATA));
                 return getGrobUnitToInchesNode().execute(scalarValue, unitId, grobList.getDataAt(index % grobList.getLength()), ctx);
             }
-            return convertToInches(scalarValue, unitId, asListOrNull(value.getAttr(UNIT_ATTR_DATA)), ctx, axisOrDim);
+            return convertToInches(scalarValue, index, unitId, asListOrNull(value.getAttr(UNIT_ATTR_DATA)), ctx, axisOrDim);
         }
 
         @Specialization(guards = "isUnitList(value)")
@@ -548,7 +548,7 @@ public class Unit {
 
         // Note the catch: newNullAMode is applied only if the axisOrDim is dimension
         private static UnitConversionContext getNewCtx(UnitConversionContext ctx, AxisOrDimension axisOrDim, int newNullAMode) {
-            return new UnitConversionContext(ctx.viewPortSize, ctx.viewPortContext, ctx.device, ctx.drawingContext, ctx.nullLayoutMode,
+            return new UnitConversionContext(ctx.viewPortSize, ctx.viewPortContext, ctx.device, ctx.gpar, ctx.nullLayoutMode,
                             axisOrDim.isDimension() ? newNullAMode : ctx.nullArithmeticMode);
         }
 
@@ -628,7 +628,7 @@ public class Unit {
                         double nullUnitValue = pureNullUnitValue((RAbstractContainer) unitxy.getDataAt(0), 0);
                         result = evaluateNullUnit(nullUnitValue, vpTransform.size.getWidth(), conversionCtx.nullLayoutMode, conversionCtx.nullArithmeticMode);
                     } else {
-                        UnitConversionContext newConversionCtx = new UnitConversionContext(vpTransform.size, vpContext, conversionCtx.device, GPar.asDrawingContext(currentGP));
+                        UnitConversionContext newConversionCtx = new UnitConversionContext(vpTransform.size, vpContext, conversionCtx.device, GPar.create(currentGP));
                         initUnitToInchesNode();
                         if (unitId == GROBWIDTH) {
                             result = unitToInchesNode.convertWidth((RAbstractContainer) unitxy.getDataAt(0), 0, newConversionCtx);
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java
index f85a04b4c4..e36fcac3d7 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/BufferedJFrameDevice.java
@@ -30,7 +30,7 @@ import java.util.ArrayList;
  * methods open/draw a 2D graphics buffer, while the buffer is open, any drawing is done in the
  * buffer not on the screen and we also record any drawing code to be able to replay it if the
  * buffer happens to loose contents, which is a possibility mentioned in the documentation. Note: we
- * rely on the fact that {@linkl DrawingContext} is immutable.
+ * rely on the fact that {@link DrawingContext} is immutable.
  */
 public class BufferedJFrameDevice implements GridDevice {
     private final JFrameDevice inner;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
index 4b42cdce8c..6b1c9451bd 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
@@ -58,10 +58,7 @@ public class GridColor {
 
     @Override
     public boolean equals(Object obj) {
-        if (!(obj instanceof GridColor)) {
-            return false;
-        }
-        return value == ((GridColor) obj).value;
+        return obj instanceof GridColor && value == ((GridColor) obj).value;
     }
 
     @Override
-- 
GitLab