diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ColorNames.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ColorNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..18eb3b0964a463ab808f6d96d6b19d9ee1951468
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ColorNames.java
@@ -0,0 +1,686 @@
+/*
+ * 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) 1997-2014, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import java.util.HashMap;
+
+final class ColorNames {
+    private static HashMap<String, String> synonymToHexName;
+
+    public static String findByName(String synonym) {
+        if (synonymToHexName == null) {
+            initialize();
+        }
+        return synonymToHexName.get(synonym);
+    }
+
+    private static void initialize() {
+        synonymToHexName = new HashMap<>(700);
+        synonymToHexName.put("white", "#FFFFFF");
+        synonymToHexName.put("aliceblue", "#F0F8FF");
+        synonymToHexName.put("antiquewhite", "#FAEBD7");
+        synonymToHexName.put("antiquewhite1", "#FFEFDB");
+        synonymToHexName.put("antiquewhite2", "#EEDFCC");
+        synonymToHexName.put("antiquewhite3", "#CDC0B0");
+        synonymToHexName.put("antiquewhite4", "#8B8378");
+        synonymToHexName.put("aquamarine", "#7FFFD4");
+        synonymToHexName.put("aquamarine1", "#7FFFD4");
+        synonymToHexName.put("aquamarine2", "#76EEC6");
+        synonymToHexName.put("aquamarine3", "#66CDAA");
+        synonymToHexName.put("aquamarine4", "#458B74");
+        synonymToHexName.put("azure", "#F0FFFF");
+        synonymToHexName.put("azure1", "#F0FFFF");
+        synonymToHexName.put("azure2", "#E0EEEE");
+        synonymToHexName.put("azure3", "#C1CDCD");
+        synonymToHexName.put("azure4", "#838B8B");
+        synonymToHexName.put("beige", "#F5F5DC");
+        synonymToHexName.put("bisque", "#FFE4C4");
+        synonymToHexName.put("bisque1", "#FFE4C4");
+        synonymToHexName.put("bisque2", "#EED5B7");
+        synonymToHexName.put("bisque3", "#CDB79E");
+        synonymToHexName.put("bisque4", "#8B7D6B");
+        synonymToHexName.put("black", "#000000");
+        synonymToHexName.put("blanchedalmond", "#FFEBCD");
+        synonymToHexName.put("blue", "#0000FF");
+        synonymToHexName.put("blue1", "#0000FF");
+        synonymToHexName.put("blue2", "#0000EE");
+        synonymToHexName.put("blue3", "#0000CD");
+        synonymToHexName.put("blue4", "#00008B");
+        synonymToHexName.put("blueviolet", "#8A2BE2");
+        synonymToHexName.put("brown", "#A52A2A");
+        synonymToHexName.put("brown1", "#FF4040");
+        synonymToHexName.put("brown2", "#EE3B3B");
+        synonymToHexName.put("brown3", "#CD3333");
+        synonymToHexName.put("brown4", "#8B2323");
+        synonymToHexName.put("burlywood", "#DEB887");
+        synonymToHexName.put("burlywood1", "#FFD39B");
+        synonymToHexName.put("burlywood2", "#EEC591");
+        synonymToHexName.put("burlywood3", "#CDAA7D");
+        synonymToHexName.put("burlywood4", "#8B7355");
+        synonymToHexName.put("cadetblue", "#5F9EA0");
+        synonymToHexName.put("cadetblue1", "#98F5FF");
+        synonymToHexName.put("cadetblue2", "#8EE5EE");
+        synonymToHexName.put("cadetblue3", "#7AC5CD");
+        synonymToHexName.put("cadetblue4", "#53868B");
+        synonymToHexName.put("chartreuse", "#7FFF00");
+        synonymToHexName.put("chartreuse1", "#7FFF00");
+        synonymToHexName.put("chartreuse2", "#76EE00");
+        synonymToHexName.put("chartreuse3", "#66CD00");
+        synonymToHexName.put("chartreuse4", "#458B00");
+        synonymToHexName.put("chocolate", "#D2691E");
+        synonymToHexName.put("chocolate1", "#FF7F24");
+        synonymToHexName.put("chocolate2", "#EE7621");
+        synonymToHexName.put("chocolate3", "#CD661D");
+        synonymToHexName.put("chocolate4", "#8B4513");
+        synonymToHexName.put("coral", "#FF7F50");
+        synonymToHexName.put("coral1", "#FF7256");
+        synonymToHexName.put("coral2", "#EE6A50");
+        synonymToHexName.put("coral3", "#CD5B45");
+        synonymToHexName.put("coral4", "#8B3E2F");
+        synonymToHexName.put("cornflowerblue", "#6495ED");
+        synonymToHexName.put("cornsilk", "#FFF8DC");
+        synonymToHexName.put("cornsilk1", "#FFF8DC");
+        synonymToHexName.put("cornsilk2", "#EEE8CD");
+        synonymToHexName.put("cornsilk3", "#CDC8B1");
+        synonymToHexName.put("cornsilk4", "#8B8878");
+        synonymToHexName.put("cyan", "#00FFFF");
+        synonymToHexName.put("cyan1", "#00FFFF");
+        synonymToHexName.put("cyan2", "#00EEEE");
+        synonymToHexName.put("cyan3", "#00CDCD");
+        synonymToHexName.put("cyan4", "#008B8B");
+        synonymToHexName.put("darkblue", "#00008B");
+        synonymToHexName.put("darkcyan", "#008B8B");
+        synonymToHexName.put("darkgoldenrod", "#B8860B");
+        synonymToHexName.put("darkgoldenrod1", "#FFB90F");
+        synonymToHexName.put("darkgoldenrod2", "#EEAD0E");
+        synonymToHexName.put("darkgoldenrod3", "#CD950C");
+        synonymToHexName.put("darkgoldenrod4", "#8B6508");
+        synonymToHexName.put("darkgray", "#A9A9A9");
+        synonymToHexName.put("darkgreen", "#006400");
+        synonymToHexName.put("darkgrey", "#A9A9A9");
+        synonymToHexName.put("darkkhaki", "#BDB76B");
+        synonymToHexName.put("darkmagenta", "#8B008B");
+        synonymToHexName.put("darkolivegreen", "#556B2F");
+        synonymToHexName.put("darkolivegreen1", "#CAFF70");
+        synonymToHexName.put("darkolivegreen2", "#BCEE68");
+        synonymToHexName.put("darkolivegreen3", "#A2CD5A");
+        synonymToHexName.put("darkolivegreen4", "#6E8B3D");
+        synonymToHexName.put("darkorange", "#FF8C00");
+        synonymToHexName.put("darkorange1", "#FF7F00");
+        synonymToHexName.put("darkorange2", "#EE7600");
+        synonymToHexName.put("darkorange3", "#CD6600");
+        synonymToHexName.put("darkorange4", "#8B4500");
+        synonymToHexName.put("darkorchid", "#9932CC");
+        synonymToHexName.put("darkorchid1", "#BF3EFF");
+        synonymToHexName.put("darkorchid2", "#B23AEE");
+        synonymToHexName.put("darkorchid3", "#9A32CD");
+        synonymToHexName.put("darkorchid4", "#68228B");
+        synonymToHexName.put("darkred", "#8B0000");
+        synonymToHexName.put("darksalmon", "#E9967A");
+        synonymToHexName.put("darkseagreen", "#8FBC8F");
+        synonymToHexName.put("darkseagreen1", "#C1FFC1");
+        synonymToHexName.put("darkseagreen2", "#B4EEB4");
+        synonymToHexName.put("darkseagreen3", "#9BCD9B");
+        synonymToHexName.put("darkseagreen4", "#698B69");
+        synonymToHexName.put("darkslateblue", "#483D8B");
+        synonymToHexName.put("darkslategray", "#2F4F4F");
+        synonymToHexName.put("darkslategray1", "#97FFFF");
+        synonymToHexName.put("darkslategray2", "#8DEEEE");
+        synonymToHexName.put("darkslategray3", "#79CDCD");
+        synonymToHexName.put("darkslategray4", "#528B8B");
+        synonymToHexName.put("darkslategrey", "#2F4F4F");
+        synonymToHexName.put("darkturquoise", "#00CED1");
+        synonymToHexName.put("darkviolet", "#9400D3");
+        synonymToHexName.put("deeppink", "#FF1493");
+        synonymToHexName.put("deeppink1", "#FF1493");
+        synonymToHexName.put("deeppink2", "#EE1289");
+        synonymToHexName.put("deeppink3", "#CD1076");
+        synonymToHexName.put("deeppink4", "#8B0A50");
+        synonymToHexName.put("deepskyblue", "#00BFFF");
+        synonymToHexName.put("deepskyblue1", "#00BFFF");
+        synonymToHexName.put("deepskyblue2", "#00B2EE");
+        synonymToHexName.put("deepskyblue3", "#009ACD");
+        synonymToHexName.put("deepskyblue4", "#00688B");
+        synonymToHexName.put("dimgray", "#696969");
+        synonymToHexName.put("dimgrey", "#696969");
+        synonymToHexName.put("dodgerblue", "#1E90FF");
+        synonymToHexName.put("dodgerblue1", "#1E90FF");
+        synonymToHexName.put("dodgerblue2", "#1C86EE");
+        synonymToHexName.put("dodgerblue3", "#1874CD");
+        synonymToHexName.put("dodgerblue4", "#104E8B");
+        synonymToHexName.put("firebrick", "#B22222");
+        synonymToHexName.put("firebrick1", "#FF3030");
+        synonymToHexName.put("firebrick2", "#EE2C2C");
+        synonymToHexName.put("firebrick3", "#CD2626");
+        synonymToHexName.put("firebrick4", "#8B1A1A");
+        synonymToHexName.put("floralwhite", "#FFFAF0");
+        synonymToHexName.put("forestgreen", "#228B22");
+        synonymToHexName.put("gainsboro", "#DCDCDC");
+        synonymToHexName.put("ghostwhite", "#F8F8FF");
+        synonymToHexName.put("gold", "#FFD700");
+        synonymToHexName.put("gold1", "#FFD700");
+        synonymToHexName.put("gold2", "#EEC900");
+        synonymToHexName.put("gold3", "#CDAD00");
+        synonymToHexName.put("gold4", "#8B7500");
+        synonymToHexName.put("goldenrod", "#DAA520");
+        synonymToHexName.put("goldenrod1", "#FFC125");
+        synonymToHexName.put("goldenrod2", "#EEB422");
+        synonymToHexName.put("goldenrod3", "#CD9B1D");
+        synonymToHexName.put("goldenrod4", "#8B6914");
+        synonymToHexName.put("gray", "#BEBEBE");
+        synonymToHexName.put("gray0", "#000000");
+        synonymToHexName.put("gray1", "#030303");
+        synonymToHexName.put("gray2", "#050505");
+        synonymToHexName.put("gray3", "#080808");
+        synonymToHexName.put("gray4", "#0A0A0A");
+        synonymToHexName.put("gray5", "#0D0D0D");
+        synonymToHexName.put("gray6", "#0F0F0F");
+        synonymToHexName.put("gray7", "#121212");
+        synonymToHexName.put("gray8", "#141414");
+        synonymToHexName.put("gray9", "#171717");
+        synonymToHexName.put("gray10", "#1A1A1A");
+        synonymToHexName.put("gray11", "#1C1C1C");
+        synonymToHexName.put("gray12", "#1F1F1F");
+        synonymToHexName.put("gray13", "#212121");
+        synonymToHexName.put("gray14", "#242424");
+        synonymToHexName.put("gray15", "#262626");
+        synonymToHexName.put("gray16", "#292929");
+        synonymToHexName.put("gray17", "#2B2B2B");
+        synonymToHexName.put("gray18", "#2E2E2E");
+        synonymToHexName.put("gray19", "#303030");
+        synonymToHexName.put("gray20", "#333333");
+        synonymToHexName.put("gray21", "#363636");
+        synonymToHexName.put("gray22", "#383838");
+        synonymToHexName.put("gray23", "#3B3B3B");
+        synonymToHexName.put("gray24", "#3D3D3D");
+        synonymToHexName.put("gray25", "#404040");
+        synonymToHexName.put("gray26", "#424242");
+        synonymToHexName.put("gray27", "#454545");
+        synonymToHexName.put("gray28", "#474747");
+        synonymToHexName.put("gray29", "#4A4A4A");
+        synonymToHexName.put("gray30", "#4D4D4D");
+        synonymToHexName.put("gray31", "#4F4F4F");
+        synonymToHexName.put("gray32", "#525252");
+        synonymToHexName.put("gray33", "#545454");
+        synonymToHexName.put("gray34", "#575757");
+        synonymToHexName.put("gray35", "#595959");
+        synonymToHexName.put("gray36", "#5C5C5C");
+        synonymToHexName.put("gray37", "#5E5E5E");
+        synonymToHexName.put("gray38", "#616161");
+        synonymToHexName.put("gray39", "#636363");
+        synonymToHexName.put("gray40", "#666666");
+        synonymToHexName.put("gray41", "#696969");
+        synonymToHexName.put("gray42", "#6B6B6B");
+        synonymToHexName.put("gray43", "#6E6E6E");
+        synonymToHexName.put("gray44", "#707070");
+        synonymToHexName.put("gray45", "#737373");
+        synonymToHexName.put("gray46", "#757575");
+        synonymToHexName.put("gray47", "#787878");
+        synonymToHexName.put("gray48", "#7A7A7A");
+        synonymToHexName.put("gray49", "#7D7D7D");
+        synonymToHexName.put("gray50", "#7F7F7F");
+        synonymToHexName.put("gray51", "#828282");
+        synonymToHexName.put("gray52", "#858585");
+        synonymToHexName.put("gray53", "#878787");
+        synonymToHexName.put("gray54", "#8A8A8A");
+        synonymToHexName.put("gray55", "#8C8C8C");
+        synonymToHexName.put("gray56", "#8F8F8F");
+        synonymToHexName.put("gray57", "#919191");
+        synonymToHexName.put("gray58", "#949494");
+        synonymToHexName.put("gray59", "#969696");
+        synonymToHexName.put("gray60", "#999999");
+        synonymToHexName.put("gray61", "#9C9C9C");
+        synonymToHexName.put("gray62", "#9E9E9E");
+        synonymToHexName.put("gray63", "#A1A1A1");
+        synonymToHexName.put("gray64", "#A3A3A3");
+        synonymToHexName.put("gray65", "#A6A6A6");
+        synonymToHexName.put("gray66", "#A8A8A8");
+        synonymToHexName.put("gray67", "#ABABAB");
+        synonymToHexName.put("gray68", "#ADADAD");
+        synonymToHexName.put("gray69", "#B0B0B0");
+        synonymToHexName.put("gray70", "#B3B3B3");
+        synonymToHexName.put("gray71", "#B5B5B5");
+        synonymToHexName.put("gray72", "#B8B8B8");
+        synonymToHexName.put("gray73", "#BABABA");
+        synonymToHexName.put("gray74", "#BDBDBD");
+        synonymToHexName.put("gray75", "#BFBFBF");
+        synonymToHexName.put("gray76", "#C2C2C2");
+        synonymToHexName.put("gray77", "#C4C4C4");
+        synonymToHexName.put("gray78", "#C7C7C7");
+        synonymToHexName.put("gray79", "#C9C9C9");
+        synonymToHexName.put("gray80", "#CCCCCC");
+        synonymToHexName.put("gray81", "#CFCFCF");
+        synonymToHexName.put("gray82", "#D1D1D1");
+        synonymToHexName.put("gray83", "#D4D4D4");
+        synonymToHexName.put("gray84", "#D6D6D6");
+        synonymToHexName.put("gray85", "#D9D9D9");
+        synonymToHexName.put("gray86", "#DBDBDB");
+        synonymToHexName.put("gray87", "#DEDEDE");
+        synonymToHexName.put("gray88", "#E0E0E0");
+        synonymToHexName.put("gray89", "#E3E3E3");
+        synonymToHexName.put("gray90", "#E5E5E5");
+        synonymToHexName.put("gray91", "#E8E8E8");
+        synonymToHexName.put("gray92", "#EBEBEB");
+        synonymToHexName.put("gray93", "#EDEDED");
+        synonymToHexName.put("gray94", "#F0F0F0");
+        synonymToHexName.put("gray95", "#F2F2F2");
+        synonymToHexName.put("gray96", "#F5F5F5");
+        synonymToHexName.put("gray97", "#F7F7F7");
+        synonymToHexName.put("gray98", "#FAFAFA");
+        synonymToHexName.put("gray99", "#FCFCFC");
+        synonymToHexName.put("gray100", "#FFFFFF");
+        synonymToHexName.put("green", "#00FF00");
+        synonymToHexName.put("green1", "#00FF00");
+        synonymToHexName.put("green2", "#00EE00");
+        synonymToHexName.put("green3", "#00CD00");
+        synonymToHexName.put("green4", "#008B00");
+        synonymToHexName.put("greenyellow", "#ADFF2F");
+        synonymToHexName.put("grey", "#BEBEBE");
+        synonymToHexName.put("grey0", "#000000");
+        synonymToHexName.put("grey1", "#030303");
+        synonymToHexName.put("grey2", "#050505");
+        synonymToHexName.put("grey3", "#080808");
+        synonymToHexName.put("grey4", "#0A0A0A");
+        synonymToHexName.put("grey5", "#0D0D0D");
+        synonymToHexName.put("grey6", "#0F0F0F");
+        synonymToHexName.put("grey7", "#121212");
+        synonymToHexName.put("grey8", "#141414");
+        synonymToHexName.put("grey9", "#171717");
+        synonymToHexName.put("grey10", "#1A1A1A");
+        synonymToHexName.put("grey11", "#1C1C1C");
+        synonymToHexName.put("grey12", "#1F1F1F");
+        synonymToHexName.put("grey13", "#212121");
+        synonymToHexName.put("grey14", "#242424");
+        synonymToHexName.put("grey15", "#262626");
+        synonymToHexName.put("grey16", "#292929");
+        synonymToHexName.put("grey17", "#2B2B2B");
+        synonymToHexName.put("grey18", "#2E2E2E");
+        synonymToHexName.put("grey19", "#303030");
+        synonymToHexName.put("grey20", "#333333");
+        synonymToHexName.put("grey21", "#363636");
+        synonymToHexName.put("grey22", "#383838");
+        synonymToHexName.put("grey23", "#3B3B3B");
+        synonymToHexName.put("grey24", "#3D3D3D");
+        synonymToHexName.put("grey25", "#404040");
+        synonymToHexName.put("grey26", "#424242");
+        synonymToHexName.put("grey27", "#454545");
+        synonymToHexName.put("grey28", "#474747");
+        synonymToHexName.put("grey29", "#4A4A4A");
+        synonymToHexName.put("grey30", "#4D4D4D");
+        synonymToHexName.put("grey31", "#4F4F4F");
+        synonymToHexName.put("grey32", "#525252");
+        synonymToHexName.put("grey33", "#545454");
+        synonymToHexName.put("grey34", "#575757");
+        synonymToHexName.put("grey35", "#595959");
+        synonymToHexName.put("grey36", "#5C5C5C");
+        synonymToHexName.put("grey37", "#5E5E5E");
+        synonymToHexName.put("grey38", "#616161");
+        synonymToHexName.put("grey39", "#636363");
+        synonymToHexName.put("grey40", "#666666");
+        synonymToHexName.put("grey41", "#696969");
+        synonymToHexName.put("grey42", "#6B6B6B");
+        synonymToHexName.put("grey43", "#6E6E6E");
+        synonymToHexName.put("grey44", "#707070");
+        synonymToHexName.put("grey45", "#737373");
+        synonymToHexName.put("grey46", "#757575");
+        synonymToHexName.put("grey47", "#787878");
+        synonymToHexName.put("grey48", "#7A7A7A");
+        synonymToHexName.put("grey49", "#7D7D7D");
+        synonymToHexName.put("grey50", "#7F7F7F");
+        synonymToHexName.put("grey51", "#828282");
+        synonymToHexName.put("grey52", "#858585");
+        synonymToHexName.put("grey53", "#878787");
+        synonymToHexName.put("grey54", "#8A8A8A");
+        synonymToHexName.put("grey55", "#8C8C8C");
+        synonymToHexName.put("grey56", "#8F8F8F");
+        synonymToHexName.put("grey57", "#919191");
+        synonymToHexName.put("grey58", "#949494");
+        synonymToHexName.put("grey59", "#969696");
+        synonymToHexName.put("grey60", "#999999");
+        synonymToHexName.put("grey61", "#9C9C9C");
+        synonymToHexName.put("grey62", "#9E9E9E");
+        synonymToHexName.put("grey63", "#A1A1A1");
+        synonymToHexName.put("grey64", "#A3A3A3");
+        synonymToHexName.put("grey65", "#A6A6A6");
+        synonymToHexName.put("grey66", "#A8A8A8");
+        synonymToHexName.put("grey67", "#ABABAB");
+        synonymToHexName.put("grey68", "#ADADAD");
+        synonymToHexName.put("grey69", "#B0B0B0");
+        synonymToHexName.put("grey70", "#B3B3B3");
+        synonymToHexName.put("grey71", "#B5B5B5");
+        synonymToHexName.put("grey72", "#B8B8B8");
+        synonymToHexName.put("grey73", "#BABABA");
+        synonymToHexName.put("grey74", "#BDBDBD");
+        synonymToHexName.put("grey75", "#BFBFBF");
+        synonymToHexName.put("grey76", "#C2C2C2");
+        synonymToHexName.put("grey77", "#C4C4C4");
+        synonymToHexName.put("grey78", "#C7C7C7");
+        synonymToHexName.put("grey79", "#C9C9C9");
+        synonymToHexName.put("grey80", "#CCCCCC");
+        synonymToHexName.put("grey81", "#CFCFCF");
+        synonymToHexName.put("grey82", "#D1D1D1");
+        synonymToHexName.put("grey83", "#D4D4D4");
+        synonymToHexName.put("grey84", "#D6D6D6");
+        synonymToHexName.put("grey85", "#D9D9D9");
+        synonymToHexName.put("grey86", "#DBDBDB");
+        synonymToHexName.put("grey87", "#DEDEDE");
+        synonymToHexName.put("grey88", "#E0E0E0");
+        synonymToHexName.put("grey89", "#E3E3E3");
+        synonymToHexName.put("grey90", "#E5E5E5");
+        synonymToHexName.put("grey91", "#E8E8E8");
+        synonymToHexName.put("grey92", "#EBEBEB");
+        synonymToHexName.put("grey93", "#EDEDED");
+        synonymToHexName.put("grey94", "#F0F0F0");
+        synonymToHexName.put("grey95", "#F2F2F2");
+        synonymToHexName.put("grey96", "#F5F5F5");
+        synonymToHexName.put("grey97", "#F7F7F7");
+        synonymToHexName.put("grey98", "#FAFAFA");
+        synonymToHexName.put("grey99", "#FCFCFC");
+        synonymToHexName.put("grey100", "#FFFFFF");
+        synonymToHexName.put("honeydew", "#F0FFF0");
+        synonymToHexName.put("honeydew1", "#F0FFF0");
+        synonymToHexName.put("honeydew2", "#E0EEE0");
+        synonymToHexName.put("honeydew3", "#C1CDC1");
+        synonymToHexName.put("honeydew4", "#838B83");
+        synonymToHexName.put("hotpink", "#FF69B4");
+        synonymToHexName.put("hotpink1", "#FF6EB4");
+        synonymToHexName.put("hotpink2", "#EE6AA7");
+        synonymToHexName.put("hotpink3", "#CD6090");
+        synonymToHexName.put("hotpink4", "#8B3A62");
+        synonymToHexName.put("indianred", "#CD5C5C");
+        synonymToHexName.put("indianred1", "#FF6A6A");
+        synonymToHexName.put("indianred2", "#EE6363");
+        synonymToHexName.put("indianred3", "#CD5555");
+        synonymToHexName.put("indianred4", "#8B3A3A");
+        synonymToHexName.put("ivory", "#FFFFF0");
+        synonymToHexName.put("ivory1", "#FFFFF0");
+        synonymToHexName.put("ivory2", "#EEEEE0");
+        synonymToHexName.put("ivory3", "#CDCDC1");
+        synonymToHexName.put("ivory4", "#8B8B83");
+        synonymToHexName.put("khaki", "#F0E68C");
+        synonymToHexName.put("khaki1", "#FFF68F");
+        synonymToHexName.put("khaki2", "#EEE685");
+        synonymToHexName.put("khaki3", "#CDC673");
+        synonymToHexName.put("khaki4", "#8B864E");
+        synonymToHexName.put("lavender", "#E6E6FA");
+        synonymToHexName.put("lavenderblush", "#FFF0F5");
+        synonymToHexName.put("lavenderblush1", "#FFF0F5");
+        synonymToHexName.put("lavenderblush2", "#EEE0E5");
+        synonymToHexName.put("lavenderblush3", "#CDC1C5");
+        synonymToHexName.put("lavenderblush4", "#8B8386");
+        synonymToHexName.put("lawngreen", "#7CFC00");
+        synonymToHexName.put("lemonchiffon", "#FFFACD");
+        synonymToHexName.put("lemonchiffon1", "#FFFACD");
+        synonymToHexName.put("lemonchiffon2", "#EEE9BF");
+        synonymToHexName.put("lemonchiffon3", "#CDC9A5");
+        synonymToHexName.put("lemonchiffon4", "#8B8970");
+        synonymToHexName.put("lightblue", "#ADD8E6");
+        synonymToHexName.put("lightblue1", "#BFEFFF");
+        synonymToHexName.put("lightblue2", "#B2DFEE");
+        synonymToHexName.put("lightblue3", "#9AC0CD");
+        synonymToHexName.put("lightblue4", "#68838B");
+        synonymToHexName.put("lightcoral", "#F08080");
+        synonymToHexName.put("lightcyan", "#E0FFFF");
+        synonymToHexName.put("lightcyan1", "#E0FFFF");
+        synonymToHexName.put("lightcyan2", "#D1EEEE");
+        synonymToHexName.put("lightcyan3", "#B4CDCD");
+        synonymToHexName.put("lightcyan4", "#7A8B8B");
+        synonymToHexName.put("lightgoldenrod", "#EEDD82");
+        synonymToHexName.put("lightgoldenrod1", "#FFEC8B");
+        synonymToHexName.put("lightgoldenrod2", "#EEDC82");
+        synonymToHexName.put("lightgoldenrod3", "#CDBE70");
+        synonymToHexName.put("lightgoldenrod4", "#8B814C");
+        synonymToHexName.put("lightgoldenrodyellow", "#FAFAD2");
+        synonymToHexName.put("lightgray", "#D3D3D3");
+        synonymToHexName.put("lightgreen", "#90EE90");
+        synonymToHexName.put("lightgrey", "#D3D3D3");
+        synonymToHexName.put("lightpink", "#FFB6C1");
+        synonymToHexName.put("lightpink1", "#FFAEB9");
+        synonymToHexName.put("lightpink2", "#EEA2AD");
+        synonymToHexName.put("lightpink3", "#CD8C95");
+        synonymToHexName.put("lightpink4", "#8B5F65");
+        synonymToHexName.put("lightsalmon", "#FFA07A");
+        synonymToHexName.put("lightsalmon1", "#FFA07A");
+        synonymToHexName.put("lightsalmon2", "#EE9572");
+        synonymToHexName.put("lightsalmon3", "#CD8162");
+        synonymToHexName.put("lightsalmon4", "#8B5742");
+        synonymToHexName.put("lightseagreen", "#20B2AA");
+        synonymToHexName.put("lightskyblue", "#87CEFA");
+        synonymToHexName.put("lightskyblue1", "#B0E2FF");
+        synonymToHexName.put("lightskyblue2", "#A4D3EE");
+        synonymToHexName.put("lightskyblue3", "#8DB6CD");
+        synonymToHexName.put("lightskyblue4", "#607B8B");
+        synonymToHexName.put("lightslateblue", "#8470FF");
+        synonymToHexName.put("lightslategray", "#778899");
+        synonymToHexName.put("lightslategrey", "#778899");
+        synonymToHexName.put("lightsteelblue", "#B0C4DE");
+        synonymToHexName.put("lightsteelblue1", "#CAE1FF");
+        synonymToHexName.put("lightsteelblue2", "#BCD2EE");
+        synonymToHexName.put("lightsteelblue3", "#A2B5CD");
+        synonymToHexName.put("lightsteelblue4", "#6E7B8B");
+        synonymToHexName.put("lightyellow", "#FFFFE0");
+        synonymToHexName.put("lightyellow1", "#FFFFE0");
+        synonymToHexName.put("lightyellow2", "#EEEED1");
+        synonymToHexName.put("lightyellow3", "#CDCDB4");
+        synonymToHexName.put("lightyellow4", "#8B8B7A");
+        synonymToHexName.put("limegreen", "#32CD32");
+        synonymToHexName.put("linen", "#FAF0E6");
+        synonymToHexName.put("magenta", "#FF00FF");
+        synonymToHexName.put("magenta1", "#FF00FF");
+        synonymToHexName.put("magenta2", "#EE00EE");
+        synonymToHexName.put("magenta3", "#CD00CD");
+        synonymToHexName.put("magenta4", "#8B008B");
+        synonymToHexName.put("maroon", "#B03060");
+        synonymToHexName.put("maroon1", "#FF34B3");
+        synonymToHexName.put("maroon2", "#EE30A7");
+        synonymToHexName.put("maroon3", "#CD2990");
+        synonymToHexName.put("maroon4", "#8B1C62");
+        synonymToHexName.put("mediumaquamarine", "#66CDAA");
+        synonymToHexName.put("mediumblue", "#0000CD");
+        synonymToHexName.put("mediumorchid", "#BA55D3");
+        synonymToHexName.put("mediumorchid1", "#E066FF");
+        synonymToHexName.put("mediumorchid2", "#D15FEE");
+        synonymToHexName.put("mediumorchid3", "#B452CD");
+        synonymToHexName.put("mediumorchid4", "#7A378B");
+        synonymToHexName.put("mediumpurple", "#9370DB");
+        synonymToHexName.put("mediumpurple1", "#AB82FF");
+        synonymToHexName.put("mediumpurple2", "#9F79EE");
+        synonymToHexName.put("mediumpurple3", "#8968CD");
+        synonymToHexName.put("mediumpurple4", "#5D478B");
+        synonymToHexName.put("mediumseagreen", "#3CB371");
+        synonymToHexName.put("mediumslateblue", "#7B68EE");
+        synonymToHexName.put("mediumspringgreen", "#00FA9A");
+        synonymToHexName.put("mediumturquoise", "#48D1CC");
+        synonymToHexName.put("mediumvioletred", "#C71585");
+        synonymToHexName.put("midnightblue", "#191970");
+        synonymToHexName.put("mintcream", "#F5FFFA");
+        synonymToHexName.put("mistyrose", "#FFE4E1");
+        synonymToHexName.put("mistyrose1", "#FFE4E1");
+        synonymToHexName.put("mistyrose2", "#EED5D2");
+        synonymToHexName.put("mistyrose3", "#CDB7B5");
+        synonymToHexName.put("mistyrose4", "#8B7D7B");
+        synonymToHexName.put("moccasin", "#FFE4B5");
+        synonymToHexName.put("navajowhite", "#FFDEAD");
+        synonymToHexName.put("navajowhite1", "#FFDEAD");
+        synonymToHexName.put("navajowhite2", "#EECFA1");
+        synonymToHexName.put("navajowhite3", "#CDB38B");
+        synonymToHexName.put("navajowhite4", "#8B795E");
+        synonymToHexName.put("navy", "#000080");
+        synonymToHexName.put("navyblue", "#000080");
+        synonymToHexName.put("oldlace", "#FDF5E6");
+        synonymToHexName.put("olivedrab", "#6B8E23");
+        synonymToHexName.put("olivedrab1", "#C0FF3E");
+        synonymToHexName.put("olivedrab2", "#B3EE3A");
+        synonymToHexName.put("olivedrab3", "#9ACD32");
+        synonymToHexName.put("olivedrab4", "#698B22");
+        synonymToHexName.put("orange", "#FFA500");
+        synonymToHexName.put("orange1", "#FFA500");
+        synonymToHexName.put("orange2", "#EE9A00");
+        synonymToHexName.put("orange3", "#CD8500");
+        synonymToHexName.put("orange4", "#8B5A00");
+        synonymToHexName.put("orangered", "#FF4500");
+        synonymToHexName.put("orangered1", "#FF4500");
+        synonymToHexName.put("orangered2", "#EE4000");
+        synonymToHexName.put("orangered3", "#CD3700");
+        synonymToHexName.put("orangered4", "#8B2500");
+        synonymToHexName.put("orchid", "#DA70D6");
+        synonymToHexName.put("orchid1", "#FF83FA");
+        synonymToHexName.put("orchid2", "#EE7AE9");
+        synonymToHexName.put("orchid3", "#CD69C9");
+        synonymToHexName.put("orchid4", "#8B4789");
+        synonymToHexName.put("palegoldenrod", "#EEE8AA");
+        synonymToHexName.put("palegreen", "#98FB98");
+        synonymToHexName.put("palegreen1", "#9AFF9A");
+        synonymToHexName.put("palegreen2", "#90EE90");
+        synonymToHexName.put("palegreen3", "#7CCD7C");
+        synonymToHexName.put("palegreen4", "#548B54");
+        synonymToHexName.put("paleturquoise", "#AFEEEE");
+        synonymToHexName.put("paleturquoise1", "#BBFFFF");
+        synonymToHexName.put("paleturquoise2", "#AEEEEE");
+        synonymToHexName.put("paleturquoise3", "#96CDCD");
+        synonymToHexName.put("paleturquoise4", "#668B8B");
+        synonymToHexName.put("palevioletred", "#DB7093");
+        synonymToHexName.put("palevioletred1", "#FF82AB");
+        synonymToHexName.put("palevioletred2", "#EE799F");
+        synonymToHexName.put("palevioletred3", "#CD6889");
+        synonymToHexName.put("palevioletred4", "#8B475D");
+        synonymToHexName.put("papayawhip", "#FFEFD5");
+        synonymToHexName.put("peachpuff", "#FFDAB9");
+        synonymToHexName.put("peachpuff1", "#FFDAB9");
+        synonymToHexName.put("peachpuff2", "#EECBAD");
+        synonymToHexName.put("peachpuff3", "#CDAF95");
+        synonymToHexName.put("peachpuff4", "#8B7765");
+        synonymToHexName.put("peru", "#CD853F");
+        synonymToHexName.put("pink", "#FFC0CB");
+        synonymToHexName.put("pink1", "#FFB5C5");
+        synonymToHexName.put("pink2", "#EEA9B8");
+        synonymToHexName.put("pink3", "#CD919E");
+        synonymToHexName.put("pink4", "#8B636C");
+        synonymToHexName.put("plum", "#DDA0DD");
+        synonymToHexName.put("plum1", "#FFBBFF");
+        synonymToHexName.put("plum2", "#EEAEEE");
+        synonymToHexName.put("plum3", "#CD96CD");
+        synonymToHexName.put("plum4", "#8B668B");
+        synonymToHexName.put("powderblue", "#B0E0E6");
+        synonymToHexName.put("purple", "#A020F0");
+        synonymToHexName.put("purple1", "#9B30FF");
+        synonymToHexName.put("purple2", "#912CEE");
+        synonymToHexName.put("purple3", "#7D26CD");
+        synonymToHexName.put("purple4", "#551A8B");
+        synonymToHexName.put("red", "#FF0000");
+        synonymToHexName.put("red1", "#FF0000");
+        synonymToHexName.put("red2", "#EE0000");
+        synonymToHexName.put("red3", "#CD0000");
+        synonymToHexName.put("red4", "#8B0000");
+        synonymToHexName.put("rosybrown", "#BC8F8F");
+        synonymToHexName.put("rosybrown1", "#FFC1C1");
+        synonymToHexName.put("rosybrown2", "#EEB4B4");
+        synonymToHexName.put("rosybrown3", "#CD9B9B");
+        synonymToHexName.put("rosybrown4", "#8B6969");
+        synonymToHexName.put("royalblue", "#4169E1");
+        synonymToHexName.put("royalblue1", "#4876FF");
+        synonymToHexName.put("royalblue2", "#436EEE");
+        synonymToHexName.put("royalblue3", "#3A5FCD");
+        synonymToHexName.put("royalblue4", "#27408B");
+        synonymToHexName.put("snewData.addlebrown", "#8B4513");
+        synonymToHexName.put("salmon", "#FA8072");
+        synonymToHexName.put("salmon1", "#FF8C69");
+        synonymToHexName.put("salmon2", "#EE8262");
+        synonymToHexName.put("salmon3", "#CD7054");
+        synonymToHexName.put("salmon4", "#8B4C39");
+        synonymToHexName.put("sandybrown", "#F4A460");
+        synonymToHexName.put("seagreen", "#2E8B57");
+        synonymToHexName.put("seagreen1", "#54FF9F");
+        synonymToHexName.put("seagreen2", "#4EEE94");
+        synonymToHexName.put("seagreen3", "#43CD80");
+        synonymToHexName.put("seagreen4", "#2E8B57");
+        synonymToHexName.put("seashell", "#FFF5EE");
+        synonymToHexName.put("seashell1", "#FFF5EE");
+        synonymToHexName.put("seashell2", "#EEE5DE");
+        synonymToHexName.put("seashell3", "#CDC5BF");
+        synonymToHexName.put("seashell4", "#8B8682");
+        synonymToHexName.put("sienna", "#A0522D");
+        synonymToHexName.put("sienna1", "#FF8247");
+        synonymToHexName.put("sienna2", "#EE7942");
+        synonymToHexName.put("sienna3", "#CD6839");
+        synonymToHexName.put("sienna4", "#8B4726");
+        synonymToHexName.put("skyblue", "#87CEEB");
+        synonymToHexName.put("skyblue1", "#87CEFF");
+        synonymToHexName.put("skyblue2", "#7EC0EE");
+        synonymToHexName.put("skyblue3", "#6CA6CD");
+        synonymToHexName.put("skyblue4", "#4A708B");
+        synonymToHexName.put("slateblue", "#6A5ACD");
+        synonymToHexName.put("slateblue1", "#836FFF");
+        synonymToHexName.put("slateblue2", "#7A67EE");
+        synonymToHexName.put("slateblue3", "#6959CD");
+        synonymToHexName.put("slateblue4", "#473C8B");
+        synonymToHexName.put("slategray", "#708090");
+        synonymToHexName.put("slategray1", "#C6E2FF");
+        synonymToHexName.put("slategray2", "#B9D3EE");
+        synonymToHexName.put("slategray3", "#9FB6CD");
+        synonymToHexName.put("slategray4", "#6C7B8B");
+        synonymToHexName.put("slategrey", "#708090");
+        synonymToHexName.put("snow", "#FFFAFA");
+        synonymToHexName.put("snow1", "#FFFAFA");
+        synonymToHexName.put("snow2", "#EEE9E9");
+        synonymToHexName.put("snow3", "#CDC9C9");
+        synonymToHexName.put("snow4", "#8B8989");
+        synonymToHexName.put("springgreen", "#00FF7F");
+        synonymToHexName.put("springgreen1", "#00FF7F");
+        synonymToHexName.put("springgreen2", "#00EE76");
+        synonymToHexName.put("springgreen3", "#00CD66");
+        synonymToHexName.put("springgreen4", "#008B45");
+        synonymToHexName.put("steelblue", "#4682B4");
+        synonymToHexName.put("steelblue1", "#63B8FF");
+        synonymToHexName.put("steelblue2", "#5CACEE");
+        synonymToHexName.put("steelblue3", "#4F94CD");
+        synonymToHexName.put("steelblue4", "#36648B");
+        synonymToHexName.put("tan", "#D2B48C");
+        synonymToHexName.put("tan1", "#FFA54F");
+        synonymToHexName.put("tan2", "#EE9A49");
+        synonymToHexName.put("tan3", "#CD853F");
+        synonymToHexName.put("tan4", "#8B5A2B");
+        synonymToHexName.put("thistle", "#D8BFD8");
+        synonymToHexName.put("thistle1", "#FFE1FF");
+        synonymToHexName.put("thistle2", "#EED2EE");
+        synonymToHexName.put("thistle3", "#CDB5CD");
+        synonymToHexName.put("thistle4", "#8B7B8B");
+        synonymToHexName.put("tomato", "#FF6347");
+        synonymToHexName.put("tomato1", "#FF6347");
+        synonymToHexName.put("tomato2", "#EE5C42");
+        synonymToHexName.put("tomato3", "#CD4F39");
+        synonymToHexName.put("tomato4", "#8B3626");
+        synonymToHexName.put("turquoise", "#40E0D0");
+        synonymToHexName.put("turquoise1", "#00F5FF");
+        synonymToHexName.put("turquoise2", "#00E5EE");
+        synonymToHexName.put("turquoise3", "#00C5CD");
+        synonymToHexName.put("turquoise4", "#00868B");
+        synonymToHexName.put("violet", "#EE82EE");
+        synonymToHexName.put("violetred", "#D02090");
+        synonymToHexName.put("violetred1", "#FF3E96");
+        synonymToHexName.put("violetred2", "#EE3A8C");
+        synonymToHexName.put("violetred3", "#CD3278");
+        synonymToHexName.put("violetred4", "#8B2252");
+        synonymToHexName.put("wheat", "#F5DEB3");
+        synonymToHexName.put("wheat1", "#FFE7BA");
+        synonymToHexName.put("wheat2", "#EED8AE");
+        synonymToHexName.put("wheat3", "#CDBA96");
+        synonymToHexName.put("wheat4", "#8B7E66");
+        synonymToHexName.put("whitesmoke", "#F5F5F5");
+        synonymToHexName.put("yellow", "#FFFF00");
+        synonymToHexName.put("yellow1", "#FFFF00");
+        synonymToHexName.put("yellow2", "#EEEE00");
+        synonymToHexName.put("yellow3", "#CDCD00");
+        synonymToHexName.put("yellow4", "#8B8B00");
+        synonymToHexName.put("yellowgreen", "#9ACD32");
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..133d02cacb9bc2a0879ea9c4ec103c354dc74c9e
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java
@@ -0,0 +1,150 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.flatten;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.fromFlat;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.identity;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.multiply;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.translation;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortLocation.VPLocationFromVPNode;
+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;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+class DoSetViewPort extends RBaseNode {
+    @Child private CastNode castScalarDouble = newCastBuilder().asDoubleVector().findFirst().buildCastNode();
+    @Child private CastNode castDoubleVector = newCastBuilder().asDoubleVector().buildCastNode();
+    @Child private CastNode castChildrenEnv = newCastBuilder().mustBe(REnvironment.class).buildCastNode();
+    @Child private Unit.UnitToInchesNode unitsToInches = Unit.UnitToInchesNode.create();
+    @Child private VPLocationFromVPNode vpLocationFromVP = new VPLocationFromVPNode();
+    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    public RList doSetViewPort(RList pushedViewPort, boolean hasParent, boolean pushing) {
+        GridState gridState = GridContext.getContext().getGridState();
+        Object[] pushedVPData = pushedViewPort.getDataWithoutCopying();
+        if (hasParent && pushing) {
+            RList parent = gridState.getViewPort();
+            pushedVPData[ViewPort.PVP_PARENT] = parent;
+            REnvironment children = (REnvironment) castChildrenEnv.execute(parent.getDataAt(ViewPort.PVP_CHILDREN));
+            safePutToEnv(pushedViewPort, pushedVPData[ViewPort.VP_NAME], children);
+        }
+
+        GridDevice currentDevice = GridContext.getContext().getCurrentDevice();
+        calcViewportTransform(pushedViewPort, pushedViewPort.getDataAt(ViewPort.PVP_PARENT), !hasParent, currentDevice, GPar.asDrawingContext(gridState.getGpar()));
+
+        // TODO: clipping
+        pushedVPData[ViewPort.PVP_CLIPRECT] = RDataFactory.createDoubleVector(new double[]{0, 0, 0, 0}, RDataFactory.COMPLETE_VECTOR);
+        pushedVPData[ViewPort.PVP_DEVWIDTHCM] = scalar(Unit.inchesToCm(currentDevice.getWidth()));
+        pushedVPData[ViewPort.PVP_DEVHEIGHTCM] = scalar(Unit.inchesToCm(currentDevice.getHeight()));
+        return pushedViewPort;
+    }
+
+    private void calcViewportTransform(RList viewPort, Object parent, boolean incremental, GridDevice device, DrawingContext drawingContext) {
+        double[][] parentTransform;
+        ViewPortContext parentContext;
+        ViewPortLocation vpl;
+        Size parentSize;
+        if (parent == null || parent == RNull.instance) {
+            parentTransform = TransformMatrix.identity();
+            parentContext = ViewPortContext.createDefault();
+            parentSize = new Size(device.getWidth(), device.getHeight());
+            vpl = vpLocationFromVP.execute(viewPort);
+        } else {
+            assert parent instanceof RList : "inconsistent data: parent of a viewport must be a list";
+            RList parentList = (RList) parent;
+            Object[] parentData = parentList.getDataWithoutCopying();
+            if (!incremental) {
+                calcViewportTransform(parentList, parentData[ViewPort.PVP_PARENT], false, device, drawingContext);
+            }
+            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 = vpContextFromVP.execute(parentList);
+
+            // TODO: gcontextFromgpar(viewportParentGPar(vp), 0, &parentgc, dd);
+            // TODO: if (....)
+            vpl = vpLocationFromVP.execute(viewPort);
+        }
+
+        UnitConversionContext conversionCtx = new UnitConversionContext(parentSize, parentContext, drawingContext);
+        double xInches = unitsToInches.convertX(vpl.x, 0, conversionCtx);
+        double yInches = unitsToInches.convertY(vpl.y, 0, conversionCtx);
+        double width = unitsToInches.convertX(vpl.width, 0, conversionCtx);
+        double height = unitsToInches.convertY(vpl.height, 0, conversionCtx);
+
+        if (!Double.isFinite(xInches) || !Double.isFinite(yInches) || !Double.isFinite(width) || !Double.isFinite(height)) {
+            error(Message.GENERIC, "non-finite location and/or size for viewport");
+        }
+
+        double xadj = GridUtils.justification(width, vpl.hjust);
+        double yadj = GridUtils.justification(height, vpl.vjust);
+
+        // Produce transform for this viewport
+        double[][] thisLocation = translation(xInches, yInches);
+        double[][] thisRotation = identity();
+        // TODO: if (viewportAngle(vp) != 0) rotation(viewportAngle(vp), thisRotation);
+
+        double[][] thisJustification = translation(xadj, yadj);
+        // Position relative to origin of rotation THEN rotate.
+        double[][] tempTransform = multiply(thisJustification, thisRotation);
+        // Translate to bottom-left corner.
+        double[][] thisTransform = multiply(tempTransform, thisLocation);
+        // Combine with parent's transform
+        double[][] transform = multiply(thisTransform, parentTransform);
+
+        // Sum up the rotation angles
+        // TODO: rotationAngle = parentAngle + viewportAngle(vp);
+        double rotationAngle = 0;
+        // TODO: Finally, allocate the rows and columns for this viewport's layout if it has one
+
+        Object[] viewPortData = viewPort.getDataWithoutCopying();
+        viewPortData[ViewPort.PVP_WIDTHCM] = scalar(Unit.inchesToCm(width));
+        viewPortData[ViewPort.PVP_HEIGHTCM] = scalar(Unit.inchesToCm(height));
+        viewPortData[ViewPort.PVP_ROTATION] = scalar(rotationAngle);
+        viewPortData[ViewPort.PVP_TRANS] = RDataFactory.createDoubleVector(flatten(transform), RDataFactory.COMPLETE_VECTOR, new int[]{3, 3});
+    }
+
+    private RAbstractDoubleVector castDoubleVector(Object obj) {
+        return (RAbstractDoubleVector) castDoubleVector.execute(obj);
+    }
+
+    private double castScalar(Object obj) {
+        return (double) castScalarDouble.execute(obj);
+    }
+
+    private static RDoubleVector scalar(double val) {
+        return RDataFactory.createDoubleVectorFromScalar(val);
+    }
+
+    private static void safePutToEnv(RList pushedViewPort, Object pushedVPDatum, REnvironment children) {
+        try {
+            children.put(RRuntime.asString(pushedVPDatum), pushedViewPort);
+        } catch (PutException e) {
+            RInternalError.shouldNotReachHere("Cannot update children environment in a view port list");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8b2359414dd6ac40bab72f1e23d2375f8f3d421
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java
@@ -0,0 +1,51 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.builtins.RBehavior;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+/**
+ * On the top of what {@link DoSetViewPort} node does, this node sets the resulting view port as the
+ * current view port in the current {@link GridState} instance. This builtin allows us to write some
+ * of the grid code in R.
+ */
+@RBuiltin(name = ".fastr.grid.doSetViewPort", parameterNames = {"vp", "hasParent", "pushing"}, kind = RBuiltinKind.INTERNAL, behavior = RBehavior.COMPLEX)
+public abstract class DoSetViewPortBuiltin extends RBuiltinNode {
+    @Child private DoSetViewPort doSetViewPort = new DoSetViewPort();
+
+    static {
+        Casts casts = new Casts(DoSetViewPortBuiltin.class);
+        casts.arg("vp").mustBe(RList.class);
+        casts.arg("hasParent").mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean());
+        casts.arg("pushing").mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean());
+    }
+
+    @Specialization
+    RNull doIt(RList pushedVP, boolean hasParent, boolean pushing) {
+        RList vp = doSetViewPort.doSetViewPort(pushedVP, hasParent, pushing);
+        GridContext.getContext().getGridState().setViewPort(vp);
+        return RNull.instance;
+    }
+
+    public static DoSetViewPortBuiltin create() {
+        return DoSetViewPortBuiltinNodeGen.create();
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..d714b28ef4950e634bac8d88ececf7d4ce802c62
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
@@ -0,0 +1,154 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import java.util.Arrays;
+
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+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.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+
+/**
+ * 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.
+ */
+public final class GPar {
+    private static final int GP_FILL = 0;
+    private static final int GP_COL = 1;
+    private static final int GP_GAMMA = 2;
+    private static final int GP_LTY = 3;
+    private static final int GP_LWD = 4;
+
+    /**
+     * Multiplier added to the final font size.
+     */
+    private static final int GP_CEX = 5;
+
+    /**
+     * Font size in points, however, the real font size will be this multiplied by {@link #GP_CEX}.
+     */
+    private static final int GP_FONTSIZE = 6;
+
+    /**
+     * Size of the line in terms of a multiply of "one line". The final real size of a line is
+     * fontsize*cex*lineheight.
+     */
+    private static final int GP_LINEHEIGHT = 7;
+    private static final int GP_FONT = 8;
+    private static final int GP_FONTFAMILY = 9;
+    private static final int GP_ALPHA = 10;
+    private static final int GP_LINEEND = 11;
+    private static final int GP_LINEJOIN = 12;
+    private static final int GP_LINEMITRE = 13;
+    private static final int GP_LEX = 14;
+    private static final int GP_FONTFACE = 15;
+    private static final int GP_LENGTH = 16;
+    private static final String[] NAMES = new String[]{
+                    "fill",
+                    "col",
+                    "gamma",
+                    "lty",
+                    "lwd",
+                    "cex",
+                    "fontsize",
+                    "lineheight",
+                    "font",
+                    "fontfamily",
+                    "alpha",
+                    "lineend",
+                    "linejoin",
+                    "linemitre",
+                    "lex",
+                    "fontface"  // TODO: could not find this name in grid sources
+    };
+    private static final RStringVector NAMES_VECTOR = (RStringVector) RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
+
+    public static RList createNew() {
+        Object[] data = new Object[GP_LENGTH];
+        Arrays.fill(data, RNull.instance);
+        data[GP_FILL] = "black";
+        data[GP_COL] = "black";
+        data[GP_GAMMA] = newDoubleVec(0);
+        data[GP_LTY] = "solid"; // TODO: LineType enum...
+        data[GP_LWD] = newDoubleVec(1);
+        data[GP_CEX] = newDoubleVec(1);
+        data[GP_FONTSIZE] = newDoubleVec(12);
+        data[GP_LINEHEIGHT] = newDoubleVec(1.0);
+        data[GP_FONT] = RDataFactory.createIntVectorFromScalar(1);  // TODO: font constants?
+        data[GP_FONTFAMILY] = ""; // means default font (probably)
+        data[GP_ALPHA] = newDoubleVec(1);
+        data[GP_LINEEND] = "round";
+        data[GP_LINEJOIN] = "round";
+        data[GP_LINEMITRE] = newDoubleVec(10);
+        data[GP_LEX] = newDoubleVec(1);
+        return RDataFactory.createList(data, NAMES_VECTOR);
+    }
+
+    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 GParDrawingContext(RList list) {
+            data = list.getDataWithoutCopying();
+        }
+
+        @Override
+        public String getColor() {
+            String result = RRuntime.asString(data[GP_COL]);
+            if (!result.startsWith("#")) {
+                result = ColorNames.findByName(result);
+            }
+            return result == null ? "#FFFFFF" : result;
+        }
+
+        @Override
+        public double getFontSize() {
+            return asDouble(data[GP_FONTSIZE]) * asDouble(data[GP_CEX]);
+        }
+
+        @Override
+        public double getLineHeight() {
+            return asDouble(data[GP_LINEHEIGHT]);
+        }
+
+        private static double asDouble(Object val) {
+            if (val instanceof Double) {
+                return (double) val;
+            } else if (val instanceof RAbstractDoubleVector) {
+                if (((RAbstractDoubleVector) val).getLength() > 0) {
+                    return ((RAbstractDoubleVector) val).getDataAt(0);
+                }
+            } else if (val instanceof Integer) {
+                return (int) val;
+            } else if (val instanceof RAbstractIntVector) {
+                if (((RAbstractIntVector) val).getLength() > 0) {
+                    return ((RAbstractIntVector) val).getDataAt(0);
+                }
+            }
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double/integer value in GPar.");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..2980378933908efae26bb8a345024acd57714529
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
@@ -0,0 +1,50 @@
+/*
+ * 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.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.library.fastrGrid.device.JFrameDevice;
+
+/**
+ * Encapsulated the acces to the global grid state.
+ */
+public final class GridContext {
+    private static final GridContext INSTANCE = new GridContext();
+    private final GridState gridState = new GridState();
+    private GridDevice currentDevice;
+
+    public static GridContext getContext() {
+        return INSTANCE;
+    }
+
+    public GridState getGridState() {
+        return gridState;
+    }
+
+    public GridDevice getCurrentDevice() {
+        if (currentDevice == null) {
+            currentDevice = new JFrameDevice();
+        }
+        return currentDevice;
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..4d77162e3d7140899a7d057d57aaaaeba42e63d5
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
@@ -0,0 +1,82 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+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;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+public final class GridState {
+    private RList gpar;
+    private RList viewPort;
+    private REnvironment gridEnv;
+    private boolean deviceInitialized;
+
+    /**
+     * Current grob being drawn (for determining the list of grobs to search when evaluating a
+     * grobwidth/height unit via gPath). May be RNull or RList.
+     */
+    private Object currentGrob;
+
+    GridState() {
+    }
+
+    public void init(REnvironment gridEnv, GridDevice currentDevice) {
+        this.gridEnv = gridEnv;
+        this.currentGrob = RNull.instance;
+        initGPar(currentDevice);
+    }
+
+    private void initGPar(GridDevice currentDevice) {
+        gpar = GPar.createNew();
+        currentDevice.initDrawingContext(GPar.asDrawingContext(gpar));
+    }
+
+    public RList getGpar() {
+        assert gridEnv != null : "GridState not initialized";
+        return gpar;
+    }
+
+    public void setGpar(RList gpar) {
+        assert gridEnv != null : "GridState not initialized";
+        this.gpar = gpar;
+    }
+
+    public boolean isDeviceInitialized() {
+        return deviceInitialized;
+    }
+
+    public void setDeviceInitialized() {
+        this.deviceInitialized = true;
+    }
+
+    public RList getViewPort() {
+        return viewPort;
+    }
+
+    public void setViewPort(RList viewPort) {
+        this.viewPort = viewPort;
+    }
+
+    public REnvironment getGridEnv() {
+        return gridEnv;
+    }
+
+    public Object getCurrentGrob() {
+        return currentGrob;
+    }
+
+    public void setCurrentGrob(Object currentGrob) {
+        this.currentGrob = currentGrob;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateGetNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateGetNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..1da60efecfd3a7867cc552a88242928cf3bc20c0
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateGetNode.java
@@ -0,0 +1,49 @@
+/*
+ * 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.library.fastrGrid;
+
+import java.util.function.Function;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+
+/**
+ * Gets a specified attribute of current {@link GridState}.
+ */
+public class GridStateGetNode extends RExternalBuiltinNode.Arg0 {
+    private final Function<GridState, Object> getter;
+
+    static {
+        Casts.noCasts(GridStateGetNode.class);
+    }
+
+    public GridStateGetNode(Function<GridState, Object> getter) {
+        this.getter = getter;
+    }
+
+    @Override
+    public Object execute() {
+        Object result = getter.apply(GridContext.getContext().getGridState());
+        assert result != null;
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateSetNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateSetNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b07fdcc38dcea0935898ab4c99f38c0a793df1a
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateSetNode.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.library.fastrGrid;
+
+import java.util.function.BiConsumer;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+/**
+ * Sets a specified attribute of current {@link GridState}.
+ */
+public final class GridStateSetNode extends RExternalBuiltinNode.Arg1 {
+    private final BiConsumer<GridState, Object> setter;
+
+    static {
+        Casts.noCasts(GridStateSetNode.class);
+    }
+
+    public static GridStateSetNode create(BiConsumer<GridState, Object> setter) {
+        return new GridStateSetNode(setter);
+    }
+
+    private GridStateSetNode(BiConsumer<GridState, Object> setter) {
+        this.setter = setter;
+    }
+
+    @Override
+    public Object execute(Object arg) {
+        setter.accept(GridContext.getContext().getGridState(), arg);
+        return RNull.instance;
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..89bf1500029e5eea1e2f113566f59ec7d0ba9157
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+final class GridUtils {
+    private GridUtils() {
+        // only static members
+    }
+
+    static double justify(double coord, double size, double justification) {
+        // justification is supposed to be either between 0 and 1
+        return coord - size * justification;
+    }
+
+    /**
+     * Returns the amount of justification required. I.e. transforms the justification from value
+     * between 0 and 1 to the value within size.
+     */
+    static double justification(double size, double justification) {
+        return -size * justification;
+    }
+
+    static double getDataAtMod(RAbstractDoubleVector vec, int idx) {
+        return vec.getDataAt(idx % vec.getLength());
+    }
+
+    @ExplodeLoop
+    static int maxLength(UnitLengthNode unitLength, RAbstractVector... units) {
+        int result = 0;
+        for (RAbstractVector unit : units) {
+            result = Math.max(result, unitLength.execute(unit));
+        }
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/IgnoredGridExternal.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/IgnoredGridExternal.java
new file mode 100644
index 0000000000000000000000000000000000000000..067193cf080edb7f114ee13e81fdeac6bb3082bd
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/IgnoredGridExternal.java
@@ -0,0 +1,47 @@
+/*
+ * 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.library.fastrGrid;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+
+/**
+ * A node for externals that we ignore, becuase we do not need to implement them or because they
+ * support functionallity we do not implement yet, especially record/replay.
+ */
+public class IgnoredGridExternal extends RExternalBuiltinNode {
+    private final Object result;
+
+    static {
+        Casts.noCasts(IgnoredGridExternal.class);
+    }
+
+    public IgnoredGridExternal(Object result) {
+        this.result = result;
+    }
+
+    @Override
+    protected Object call(RArgsValuesAndNames args) {
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec70939be01e99aac567db6a5aa4a6c1a1c7a6ac
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java
@@ -0,0 +1,50 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.library.fastrGrid.ViewPort.InitViewPortNode;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public class LGridDirty extends RExternalBuiltinNode {
+    @Child private InitViewPortNode initViewPort = new InitViewPortNode();
+    private final ConditionProfile initViewPortProfile = ConditionProfile.createCountingProfile();
+
+    static {
+        Casts.noCasts(LGridDirty.class);
+    }
+
+    @Override
+    public Object call(VirtualFrame frame, RArgsValuesAndNames args) {
+        GridState gridState = GridContext.getContext().getGridState();
+        if (!gridState.isDeviceInitialized()) {
+            CompilerDirectives.transferToInterpreter();
+            GridContext.getContext().getCurrentDevice().openNewPage();
+            gridState.setDeviceInitialized();
+        }
+        if (initViewPortProfile.profile(gridState.getViewPort() == null)) {
+            // this rarely happens, but we do not have a slow-path implementation (yet)
+            gridState.setViewPort(initViewPort.execute(frame));
+        }
+        return RNull.instance;
+    }
+
+    @Override
+    protected Object call(RArgsValuesAndNames args) {
+        // shadowed by the VirtualFrame overload
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java
new file mode 100644
index 0000000000000000000000000000000000000000..58903fc8bec533444e66e6ee8ae90c25a7bbc9f6
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java
@@ -0,0 +1,35 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+public abstract class LInitGrid extends RExternalBuiltinNode.Arg1 {
+    static {
+        Casts casts = new Casts(LInitGrid.class);
+        casts.arg(0).mustBe(REnvironment.class);
+    }
+
+    public static LInitGrid create() {
+        return LInitGridNodeGen.create();
+    }
+
+    @Specialization
+    public Object doEnv(REnvironment gridEnv) {
+        GridContext context = GridContext.getContext();
+        context.getGridState().init(gridEnv, context.getCurrentDevice());
+        return RNull.instance;
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..83a82b0a4726205cc96a81a1c64e16364883cd0a
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java
@@ -0,0 +1,37 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.library.fastrGrid.ViewPort.InitViewPortNode;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public class LInitViewPortStack extends RExternalBuiltinNode {
+    @Child private InitViewPortNode initViewPortNode = new InitViewPortNode();
+    static {
+        Casts.noCasts(LInitViewPortStack.class);
+    }
+
+    @Override
+    public Object call(VirtualFrame frame, @SuppressWarnings("unused") RArgsValuesAndNames args) {
+        initViewPortNode.execute(frame);
+        return RNull.instance;
+    }
+
+    @Override
+    protected Object call(RArgsValuesAndNames args) {
+        throw RInternalError.shouldNotReachHere("shadowed by the overload with VirtualFrame");
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java
new file mode 100644
index 0000000000000000000000000000000000000000..87a5269c3424f1193253b9a73c4aa1b79e39995a
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java
@@ -0,0 +1,109 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
+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.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+/**
+ * Note: the third parameter contains sequences {@code 1:max(length(x),length(y))}, where the
+ * 'length' dispatches to S3 method giving us unit length like
+ * {@link com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode}.
+ */
+public abstract class LLines extends RExternalBuiltinNode.Arg4 {
+    @Child private CastNode toIntVector = newCastBuilder().mustBe(integerValue()).boxPrimitive().asIntegerVector().buildCastNode();
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    static {
+        Casts casts = new Casts(LLines.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(RList.class);
+    }
+
+    public static LLines create() {
+        return LLinesNodeGen.create();
+    }
+
+    @Specialization
+    Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, Object arrowIgnored) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
+        ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+
+        // Convert the list of vectors of indexes to type-safe array and calculate the max length of
+        // the vectors.
+        RAbstractIntVector[] unitIndexesList = new RAbstractIntVector[lengths.getLength()];
+        int maxIndexesLen = 0;
+        for (int i = 0; i < lengths.getLength(); i++) {
+            unitIndexesList[i] = (RAbstractIntVector) toIntVector.execute(lengths.getDataAt(i));
+            maxIndexesLen = Math.max(maxIndexesLen, unitIndexesList[i].getLength());
+        }
+
+        double[] xx = new double[maxIndexesLen];
+        double[] yy = new double[maxIndexesLen];
+        for (RAbstractIntVector unitIndexes : unitIndexesList) {
+            boolean oldIsFinite = false;
+            int start = 0;
+            int unitIndexesLen = unitIndexes.getLength();
+            // following loop finds series of valid points (finite x and y values) and draws each
+            // such series as a polyline
+            for (int i = 0; i < unitIndexesLen; i++) {
+                int unitIndex = unitIndexes.getDataAt(i) - 1;   // coverting R's 1-based index
+                Point origLoc = Point.fromUnits(unitToInches, x, y, unitIndex, conversionCtx);
+                Point loc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
+                xx[i] = loc.x;
+                yy[i] = loc.y;
+                boolean currIsFinite = loc.isFinite();
+                boolean lastIter = i == (unitIndexesLen - 1);
+                if (currIsFinite && !oldIsFinite) {
+                    start = i; // start a new series
+                } else if (oldIsFinite && (!currIsFinite || lastIter)) {
+                    // draw the previous points series because
+                    // (1) current is invalid point. Note: in (one of) the next iteration(s), the
+                    // oldIsFinite will be false and we will update the start and start a new series
+                    // (2) we are in the last iteration
+                    if (lastIter || i - start > 1) {
+                        // we draw only if the previous series of points was at least of length 3 or
+                        // it's last iteration. This seems slightly weird, but that's how GnuR seems
+                        // to work
+                        dev.drawPolyLines(drawingCtx, xx, yy, start, (i - start) + 1);
+                    }
+                }
+                oldIsFinite = currIsFinite;
+            }
+        }
+
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..727e10ed9104d8f6b6ef1cd63c98fb9ed1068954
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java
@@ -0,0 +1,27 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public class LNewPage extends RExternalBuiltinNode.Arg0 {
+    static {
+        Casts.noCasts(LNewPage.class);
+    }
+
+    @Override
+    public Object execute() {
+        GridContext.getContext().getCurrentDevice().openNewPage();
+        return RNull.instance;
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..e775dde0535ed7070523f30b7b07b8d81826ebab
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
@@ -0,0 +1,74 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
+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;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LRect extends RExternalBuiltinNode.Arg6 {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    static {
+        Casts casts = new Casts(LRect.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+        casts.arg(3).mustBe(abstractVectorValue());
+        casts.arg(4).mustBe(numericValue()).asDoubleVector();
+        casts.arg(5).mustBe(numericValue()).asDoubleVector();
+    }
+
+    public static LRect create() {
+        return LRectNodeGen.create();
+    }
+
+    @Specialization
+    public Object execute(RAbstractVector xVec, RAbstractVector yVec, RAbstractVector wVec, RAbstractVector hVec, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
+        ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, xVec, yVec, wVec, hVec);
+        for (int i = 0; i < length; i++) {
+            double w = unitToInches.convertX(wVec, i, conversionCtx);
+            double h = unitToInches.convertY(hVec, i, conversionCtx);
+            // Note: once this is factored to drawing/recording: this transformation is necessary
+            // only for drawing
+            Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
+            Point transLoc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
+            Point loc = transLoc.justify(w, h, getDataAtMod(hjust, i), getDataAtMod(vjust, i));
+            dev.drawRect(drawingCtx, loc.x, loc.y, w, h);
+        }
+        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
new file mode 100644
index 0000000000000000000000000000000000000000..3aca3f3afc573ff7ecbffdd36790bb2c506d18cf
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java
@@ -0,0 +1,77 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
+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;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+/**
+ * Gets (vectors of) 4 coordinates (two points) and draws a line between them, unlike {@link LLines}
+ * which gets a vector of points and connects them all.
+ */
+public abstract class LSegments extends RExternalBuiltinNode.Arg5 {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    static {
+        Casts casts = new Casts(LSegments.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+        casts.arg(3).mustBe(abstractVectorValue());
+    }
+
+    public static LSegments create() {
+        return LSegmentsNodeGen.create();
+    }
+
+    @Specialization
+    Object doSegments(RAbstractVector x0, RAbstractVector y0, RAbstractVector x1, RAbstractVector y1, Object arrowIgnored) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
+        ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, x0, y0, x1, y1);
+        double[] xx = new double[2];
+        double[] yy = new double[2];
+        for (int i = 0; i < length; i++) {
+            Point loc1 = TransformMatrix.transLocation(Point.fromUnits(unitToInches, x0, y0, i, conversionCtx), vpTransform.transform);
+            Point loc2 = TransformMatrix.transLocation(Point.fromUnits(unitToInches, x1, y1, i, conversionCtx), vpTransform.transform);
+            if (!loc1.isFinite() || !loc2.isFinite()) {
+                continue;
+            }
+            xx[0] = loc1.x;
+            xx[1] = loc2.x;
+            yy[0] = loc1.y;
+            yy[1] = loc2.y;
+            dev.drawPolyLines(drawingCtx, xx, yy, 0, 2);
+        }
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc3b9082a49f1a7aaf43494347ad287d187a73ce
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java
@@ -0,0 +1,125 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
+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;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+/**
+ * Note: the third parameter contains sequences {@code 1:max(length(x),length(y))}, where the
+ * 'length' dispatches to S3 method giving us unit length like {@link Unit.UnitLengthNode}.
+ */
+public abstract class LText extends RExternalBuiltinNode.Arg7 {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    static {
+        Casts casts = new Casts(LText.class);
+        // TODO: expressions and maybe other types should have special handling, not only simple
+        // String coercion
+        casts.arg(0).asStringVector();
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+        casts.arg(3).mustBe(numericValue()).asDoubleVector();
+        casts.arg(4).mustBe(numericValue()).asDoubleVector();
+        casts.arg(5).mustBe(numericValue()).asDoubleVector();
+    }
+
+    public static LText create() {
+        return LTextNodeGen.create();
+    }
+
+    @Specialization
+    Object drawText(RAbstractStringVector text, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, RAbstractDoubleVector rotation,
+                    Object checkOverlapIgnored) {
+        if (text.getLength() == 0) {
+            return RNull.instance;
+        }
+
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
+        ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, x, y);
+        for (int i = 0; i < length; i++) {
+            Point loc = TransformMatrix.transLocation(Point.fromUnits(unitToInches, x, y, i, conversionCtx), vpTransform.transform);
+            text(loc.x, loc.y, text.getDataAt(i % text.getLength()), getDataAtMod(hjust, i), getDataAtMod(vjust, i), getDataAtMod(rotation, i), drawingCtx, dev);
+        }
+        return RNull.instance;
+    }
+
+    // transcribed from engine.c
+
+    private void text(double x, double y, String text, double xadjIn, double yadj, double rotation, DrawingContext drawingCtx, GridDevice device) {
+        if (!Double.isFinite(yadj)) {
+            throw new RuntimeException("Not implemented: 'exact' vertical centering, see engine.c:1700");
+        }
+        double xadj = Double.isFinite(xadjIn) ? xadjIn : 0.5;
+
+        double radRotation = Math.toRadians(rotation);
+        double cosRot = Math.cos(radRotation);
+        double sinRot = Math.sin(radRotation);
+        String[] lines = text.split("\n");
+        for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
+            double xoff;
+            double yoff;
+            if (lines.length == 1) {
+                // simplification for single line
+                xoff = x;
+                yoff = y;
+            } else {
+                yoff = (1 - yadj) * (lines.length - 1) - lineIdx;
+                // TODO: in the original the following formula uses "dd->dev->cra[1]"
+                yoff *= (drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+                xoff = -yoff * sinRot;
+                yoff = yoff * cosRot;
+                xoff = x + xoff;
+                yoff = y + yoff;
+            }
+
+            double xleft = xoff;
+            double ybottom = yoff;
+            // now determine bottom-left for THIS line
+            if (xadj != 0.0 || yadj != 0.0) {
+                // otherwise simply the initial values for xleft and ybottom are OK
+                double width = device.getStringWidth(drawingCtx, lines[lineIdx]);
+                double height = device.getStringHeight(drawingCtx, lines[lineIdx]);
+                xleft = xoff - (xadj) * width * cosRot + yadj * height * sinRot;
+                ybottom = yoff - (xadj) * width * sinRot - yadj * height * cosRot;
+            }
+
+            device.drawString(drawingCtx, xleft, ybottom, rotation, lines[lineIdx]);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e51c4b4d8704de111da056926b350af43c9c544
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java
@@ -0,0 +1,50 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public abstract class LUpViewPort extends RExternalBuiltinNode.Arg1 {
+    @Child private CastNode castParentToViewPort = newCastBuilder().mustBe(RList.class, Message.GENERIC, "cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)").buildCastNode();
+
+    static {
+        Casts casts = new Casts(LUpViewPort.class);
+        casts.arg(0).mustBe(numericValue()).asIntegerVector().findFirst(1).mustBe(gte(1));
+    }
+
+    public static LUpViewPort create() {
+        return LUpViewPortNodeGen.create();
+    }
+
+    @Specialization
+    Object upViewPort(int n) {
+        GridState gridState = GridContext.getContext().getGridState();
+        RList newViewPort = gridState.getViewPort();
+        for (int i = 0; i < n; i++) {
+            newViewPort = (RList) castParentToViewPort.execute(newViewPort.getDataAt(ViewPort.PVP_PARENT));
+        }
+        gridState.setViewPort(newViewPort);
+
+        // TODO: device changed? => calcViewportTransform for newViewPort
+        // TODO: update the clipping region
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
new file mode 100644
index 0000000000000000000000000000000000000000..7bd1158d1dd2127d23f46a56db5fb35f230214e0
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
@@ -0,0 +1,51 @@
+/*
+ * 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.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public final class Point {
+    final double x;
+    final double y;
+
+    public Point(double x, double y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public static Point fromUnits(UnitToInchesNode unitToInches, RAbstractVector x, RAbstractVector y, int index, UnitConversionContext ctx) {
+        double newX = unitToInches.convertX(x, index, ctx);
+        double newY = unitToInches.convertY(y, index, ctx);
+        return new Point(newX, newY);
+    }
+
+    public Point justify(double width, double height, double hjust, double vjust) {
+        return new Point(GridUtils.justify(x, width, hjust), GridUtils.justify(y, height, vjust));
+    }
+
+    public boolean isFinite() {
+        return Double.isFinite(x) && Double.isFinite(y);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
new file mode 100644
index 0000000000000000000000000000000000000000..803a07eb122560121ebb611b455ad7e7fd0acc70
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
@@ -0,0 +1,41 @@
+/*
+ * 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.library.fastrGrid;
+
+public final class Size {
+    private final double width;
+    private final double height;
+
+    public Size(double width, double height) {
+        this.width = width;
+        this.height = height;
+    }
+
+    public double getWidth() {
+        return width;
+    }
+
+    public double getHeight() {
+        return height;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b66ab2bae3ce622c2ab8cf2672f51824147b9c7
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java
@@ -0,0 +1,101 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+/**
+ * Operations on transformation (3x3) matrices.
+ */
+final class TransformMatrix {
+    private TransformMatrix() {
+        // only static members
+    }
+
+    static double[][] translation(double tx, double ty) {
+        double[][] m = identity();
+        m[2][0] = tx;
+        m[2][1] = ty;
+        return m;
+    }
+
+    static double[][] multiply(double[][] m1, double[][] m2) {
+        double[][] m = new double[3][3];
+        m[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0] + m1[0][2] * m2[2][0];
+        m[0][1] = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1] + m1[0][2] * m2[2][1];
+        m[0][2] = m1[0][0] * m2[0][2] + m1[0][1] * m2[1][2] + m1[0][2] * m2[2][2];
+        m[1][0] = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0] + m1[1][2] * m2[2][0];
+        m[1][1] = m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1] + m1[1][2] * m2[2][1];
+        m[1][2] = m1[1][0] * m2[0][2] + m1[1][1] * m2[1][2] + m1[1][2] * m2[2][2];
+        m[2][0] = m1[2][0] * m2[0][0] + m1[2][1] * m2[1][0] + m1[2][2] * m2[2][0];
+        m[2][1] = m1[2][0] * m2[0][1] + m1[2][1] * m2[1][1] + m1[2][2] * m2[2][1];
+        m[2][2] = m1[2][0] * m2[0][2] + m1[2][1] * m2[1][2] + m1[2][2] * m2[2][2];
+        return m;
+    }
+
+    static double[][] identity() {
+        double[][] result = new double[3][3];
+        result[0][0] = 1;
+        result[1][1] = 1;
+        result[2][2] = 1;
+        return result;
+    }
+
+    private static double[] location(double x, double y) {
+        return new double[]{x, y, 1};
+    }
+
+    private static double[] transLocation(double[] location, double[][] m) {
+        double[] res = new double[3];
+        res[0] = location[0] * m[0][0] + location[1] * m[1][0] + location[2] * m[2][0];
+        res[1] = location[0] * m[0][1] + location[1] * m[1][1] + location[2] * m[2][1];
+        res[2] = location[0] * m[0][2] + location[1] * m[1][2] + location[2] * m[2][2];
+        return res;
+    }
+
+    static Point transLocation(Point loc, double[][] m) {
+        double[] newLoc = transLocation(location(loc.x, loc.y), m);
+        return new Point(locationX(newLoc), locationY(newLoc));
+    }
+
+    private static double locationX(double[] loc) {
+        return loc[0];
+    }
+
+    private static double locationY(double[] loc) {
+        return loc[1];
+    }
+
+    /**
+     * Transforms the internal double matrix to R matrix flat array.
+     */
+    static double[] flatten(double[][] m) {
+        double[] res = new double[9];
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                res[i + j * 3] = m[i][j];
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Reverse operation to {@link #flatten(double[][])}.
+     */
+    static double[][] fromFlat(double[] flat) {
+        double[][] res = new double[3][3];
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                res[i][j] = flat[i + j * 3];
+            }
+        }
+        return res;
+    }
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..c12899186e4ace6996c10383a5a662911a8cbf5e
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
@@ -0,0 +1,217 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitElementAtNodeGen;
+import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitLengthNodeGen;
+import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitToInchesNodeGen;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+/**
+ * Note: internally in FastR Grid everything is in inches. However, some lists that are exposed to
+ * the R code should contain values in centimeters, we convert such values immediatelly once they
+ * enter our system.
+ */
+public class Unit {
+    private static final String VALID_UNIT_ATTR = "valid.unit";
+
+    private static final int NPC = 0;
+    public static final int CM = 1;
+    public static final int INCHES = 2;
+    private static final int LINES = 3;
+    private static final int NATIVE = 4;
+    public static final int NULL = 5; /* only used in layout specifications (?) */
+    public static final int SNPC = 6;
+    public static final int MM = 7;
+    /*
+     * Some units based on TeX's definition thereof
+     */
+    public static final int POINTS = 8; /* 72.27 pt = 1 in */
+    public static final int PICAS = 9; /* 1 pc = 12 pt */
+    public static final int BIGPOINTS = 10; /* 72 bp = 1 in */
+    public static final int DIDA = 11; /* 1157 dd = 1238 pt */
+    public static final int CICERO = 12; /* 1 cc = 12 dd */
+    public static final int SCALEDPOINTS = 13; /* 65536 sp = 1pt */
+    /*
+     * Some units which require an object to query for a value.
+     */
+    public static final int STRINGWIDTH = 14;
+    public static final int STRINGHEIGHT = 15;
+    public static final int STRINGASCENT = 16;
+    public static final int STRINGDESCENT = 17;
+    /*
+     * public static final int LINES now means multiples of the line height. This is multiples of
+     * the font size.
+     */
+    public static final int CHAR = 18;
+    public static final int GROBX = 19;
+    public static final int GROBY = 20;
+    public static final int GROBWIDTH = 21;
+    public static final int GROBHEIGHT = 22;
+    public static final int GROBASCENT = 23;
+    public static final int GROBDESCENT = 24;
+    /*
+     * No longer used
+     */
+    private static final int MYLINES = 103;
+    public static final int MYCHAR = 104;
+    public static final int MYSTRINGWIDTH = 105;
+    public static final int MYSTRINGHEIGHT = 106;
+
+    private static final double CM_IN_INCH = 2.54;
+
+    public static double inchesToCm(double inches) {
+        return inches * CM_IN_INCH;
+    }
+
+    public static double cmToInches(double cm) {
+        return cm / CM_IN_INCH;
+    }
+
+    public static UnitLengthNode createLengthNode() {
+        return UnitLengthNode.create();
+    }
+
+    public static UnitToInchesNode createToInchesNode() {
+        return UnitToInchesNode.create();
+    }
+
+    public abstract static class UnitNodeBase extends Node {
+        @Child private InheritsCheckNode inheritsCheckNode = new InheritsCheckNode("unit.arithmetic");
+
+        boolean isArithmetic(Object obj) {
+            return obj instanceof RList && inheritsCheckNode.execute(obj);
+        }
+    }
+
+    /**
+     * A unit object can represent more or fewer values that the number of elements underlying list
+     * or vector. This node gives the length if the unit in a sense of the upper limit on what can
+     * be used as an index for {@link UnitElementAtNode}.
+     */
+    public abstract static class UnitLengthNode extends UnitNodeBase {
+        public static UnitLengthNode create() {
+            return UnitLengthNodeGen.create();
+        }
+
+        public abstract int execute(RAbstractContainer vector);
+
+        @Specialization(guards = "!isArithmetic(value)")
+        int doNormal(RAbstractContainer value) {
+            return value.getLength();
+        }
+
+        @Specialization(guards = "isArithmetic(list)")
+        int doArithmetic(RList list) {
+            throw RInternalError.unimplemented("Length for arithmetic units");
+        }
+    }
+
+    /**
+     * @see UnitLengthNode
+     */
+    public abstract static class UnitElementAtNode extends UnitNodeBase {
+        @Child private CastNode castToDouble = newCastBuilder().asDoubleVector().buildCastNode();
+
+        public static UnitElementAtNode create() {
+            return UnitElementAtNodeGen.create();
+        }
+
+        public abstract double execute(RAbstractContainer vector, int index);
+
+        @Specialization(guards = "!isArithmetic(value)")
+        double doNormal(RAbstractContainer value, int index) {
+            return ((RAbstractDoubleVector) castToDouble.execute(value)).getDataAt(index);
+        }
+
+        @Specialization(guards = "isArithmetic(list)")
+        double doArithmetic(RList list, int index) {
+            throw RInternalError.unimplemented("UnitElementAt for arithmetic units");
+        }
+    }
+
+    /**
+     * Wraps the data necessary for converting a unit to another unit.
+     */
+    public static final class UnitConversionContext {
+        public final Size viewPortSize;
+        public final ViewPortContext viewPortContext;
+        public final DrawingContext drawingContext;
+
+        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, DrawingContext drawingContext) {
+            this.viewPortSize = viewPortSize;
+            this.viewPortContext = viewPortContext;
+            this.drawingContext = drawingContext;
+        }
+    }
+
+    /**
+     * Normalizes grid unit object to a double value in inches. For convenience the index is
+     * interpreted as cyclic unlike in {@link UnitElementAtNode}.
+     */
+    public abstract static class UnitToInchesNode extends UnitNodeBase {
+        @Child private CastNode castUnitId = newCastBuilder().mustBe(numericValue()).asIntegerVector().findFirst().buildCastNode();
+        @Child private UnitElementAtNode elementAtNode = UnitElementAtNode.create();
+
+        public static UnitToInchesNode create() {
+            return UnitToInchesNodeGen.create();
+        }
+
+        public double convertX(RAbstractContainer vector, int index, UnitConversionContext conversionCtx) {
+            return execute(vector, index, conversionCtx.viewPortSize.getWidth(), conversionCtx.viewPortContext.xscalemin, conversionCtx.viewPortContext.xscalemax, conversionCtx.drawingContext);
+        }
+
+        public double convertY(RAbstractContainer vector, int index, UnitConversionContext conversionCtx) {
+            return execute(vector, index, conversionCtx.viewPortSize.getHeight(), conversionCtx.viewPortContext.yscalemin, conversionCtx.viewPortContext.yscalemax, conversionCtx.drawingContext);
+        }
+
+        public abstract double execute(RAbstractContainer vector, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx);
+
+        @Specialization(guards = "!isArithmetic(value)")
+        double doNormal(RAbstractContainer value, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+            int unitId = (Integer) castUnitId.execute(value.getAttr(VALID_UNIT_ATTR));
+            return convert(elementAtNode.execute(value, index % value.getLength()), unitId, vpSize, scalemin, scalemax, drawingCtx);
+        }
+
+        @Specialization(guards = "isArithmetic(list)")
+        double doArithmetic(RList list, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+            throw RInternalError.unimplemented("UnitToInches for arithmetic units");
+        }
+
+        private static double convert(double value, int unitId, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+            switch (unitId) {
+                case NATIVE:
+                    return ((value - scalemin) / (scalemax - scalemin)) * vpSize;
+                case NPC:
+                    return value * vpSize;
+                case LINES:
+                case MYLINES:
+                    return (value * drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+
+                default:
+                    throw RInternalError.unimplemented("unit type " + unitId + " in UnitToInches");
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..820e2ef11961da3c7a695c311ff4b4f3d353ad1d
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
@@ -0,0 +1,85 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RList;
+
+/**
+ * There is a notion of a view point, which is an ordinary list that user creates to define a view
+ * port. One such list is pushed using {@code pushViewpoint} it is transformed to a 'pushed
+ * viewpoint', which is a copy of the original view point and it has some additional attributes.
+ */
+class ViewPort {
+    /*
+     * Structure of a viewport
+     */
+    public static final int VP_X = 0;
+    public static final int VP_Y = 1;
+    public static final int VP_WIDTH = 2;
+    public static final int VP_HEIGHT = 3;
+    public static final int VP_JUST = 4;
+    public static final int VP_GP = 5;
+    public static final int VP_CLIP = 6;
+    public static final int VP_XSCALE = 7;
+    public static final int VP_YSCALE = 8;
+    public static final int VP_ANGLE = 9;
+    public static final int VP_LAYOUT = 10;
+    public static final int VP_LPOSROW = 11;
+    public static final int VP_LPOSCOL = 12;
+    public static final int VP_VALIDJUST = 13;
+    public static final int VP_VALIDLPOSROW = 14;
+    public static final int VP_VALIDLPOSCOL = 15;
+    public static final int VP_NAME = 16;
+    /*
+     * Additional structure of a pushedvp
+     */
+    public static final int PVP_PARENTGPAR = 17;
+    private static final int PVP_GPAR = 18;
+    public static final int PVP_TRANS = 19;
+    public static final int PVP_WIDTHS = 20;
+    public static final int PVP_HEIGHTS = 21;
+    public static final int PVP_WIDTHCM = 22;
+    public static final int PVP_HEIGHTCM = 23;
+    public static final int PVP_ROTATION = 24;
+    public static final int PVP_CLIPRECT = 25;
+    public static final int PVP_PARENT = 26;
+    public static final int PVP_CHILDREN = 27;
+    public static final int PVP_DEVWIDTHCM = 28;
+    public static final int PVP_DEVHEIGHTCM = 29;
+
+    public static final class InitViewPortNode extends Node {
+        @Child private ReadVariableNode readGridTopLevel = ReadVariableNode.create("grid.top.level.vp");
+        @Child private RExplicitCallNode callNode = RExplicitCallNode.create();
+        @Child private DoSetViewPort doSetViewPort = new DoSetViewPort();
+
+        public RList execute(VirtualFrame frame) {
+            RFunction gridTopLevel = (RFunction) readGridTopLevel.execute(frame);
+            RList topVP = (RList) callNode.execute(frame, gridTopLevel, RArgsValuesAndNames.EMPTY);
+
+            GridDevice device = GridContext.getContext().getCurrentDevice();
+            // TODO: properly set the scale according to the current device
+            Object[] data = topVP.getDataWithoutCopying();
+            data[ViewPort.VP_XSCALE] = RDataFactory.createDoubleVector(new double[]{0, device.getWidth()}, RDataFactory.COMPLETE_VECTOR);
+            data[ViewPort.VP_YSCALE] = RDataFactory.createDoubleVector(new double[]{0, device.getHeight()}, RDataFactory.COMPLETE_VECTOR);
+            data[ViewPort.PVP_GPAR] = GridContext.getContext().getGridState().getGpar();
+            return doSetViewPort.doSetViewPort(topVP, false, true);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..e07043ce1c1a3e4e94be522d83d5eff8794e0406
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java
@@ -0,0 +1,58 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+public final class ViewPortContext {
+    public double xscalemin;
+    public double yscalemin;
+    public double xscalemax;
+    public double yscalemax;
+
+    private ViewPortContext() {
+    }
+
+    public static ViewPortContext createDefault() {
+        ViewPortContext result = new ViewPortContext();
+        result.xscalemin = 0;
+        result.yscalemin = 0;
+        result.xscalemax = 1;
+        result.yscalemax = 1;
+        return result;
+    }
+
+    public static final class VPContextFromVPNode extends Node {
+        @Child private CastNode castVector = newCastBuilder().asDoubleVector().mustBe(Predef.size(2)).buildCastNode();
+
+        public ViewPortContext execute(RList viewPort) {
+            ViewPortContext result = new ViewPortContext();
+            RAbstractDoubleVector x = castVec(viewPort.getDataAt(ViewPort.VP_XSCALE));
+            result.xscalemin = x.getDataAt(0);
+            result.xscalemax = x.getDataAt(1);
+            RAbstractDoubleVector y = castVec(viewPort.getDataAt(ViewPort.VP_YSCALE));
+            result.yscalemin = y.getDataAt(0);
+            result.yscalemax = y.getDataAt(1);
+            return result;
+        }
+
+        private RAbstractDoubleVector castVec(Object val) {
+            return (RAbstractDoubleVector) castVector.execute(val);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java
new file mode 100644
index 0000000000000000000000000000000000000000..894d8d90e134561eb12bf81bfbeb81156eed468b
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java
@@ -0,0 +1,55 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+/**
+ * The vectors in this class represent a unit objects, therefore we cannot just have a double value
+ * for them. However, the unit object should contain only single value.
+ */
+public class ViewPortLocation {
+    public RAbstractDoubleVector x;
+    public RAbstractDoubleVector y;
+    public RAbstractDoubleVector width;
+    public RAbstractDoubleVector height;
+    public double hjust;
+    public double vjust;
+
+    public static final class VPLocationFromVPNode extends Node {
+        @Child private CastNode castDoubleVector = newCastBuilder().mustBe(numericValue()).asDoubleVector().buildCastNode();
+        @Child private CastNode castJustVector = newCastBuilder().mustBe(numericValue()).asDoubleVector().mustBe(size(2)).buildCastNode();
+
+        public ViewPortLocation execute(RList viewPort) {
+            ViewPortLocation r = new ViewPortLocation();
+            r.x = vec(viewPort.getDataAt(ViewPort.VP_X));
+            r.y = vec(viewPort.getDataAt(ViewPort.VP_Y));
+            r.width = vec(viewPort.getDataAt(ViewPort.VP_WIDTH));
+            r.height = vec(viewPort.getDataAt(ViewPort.VP_HEIGHT));
+            RAbstractDoubleVector just = (RAbstractDoubleVector) castJustVector.execute(viewPort.getDataAt(ViewPort.VP_VALIDJUST));
+            r.hjust = just.getDataAt(0);
+            r.vjust = just.getDataAt(1);
+            return r;
+        }
+
+        private RAbstractDoubleVector vec(Object val) {
+            return (RAbstractDoubleVector) castDoubleVector.execute(val);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java
new file mode 100644
index 0000000000000000000000000000000000000000..016e90247a4748515ccd309d04d6e4236f45cfd0
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java
@@ -0,0 +1,53 @@
+/*
+ * 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) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+/**
+ * Holds the data of a viewport needed to perform transformations.
+ */
+public final class ViewPortTransform {
+    private final double rotationAngle;
+    public final double[][] transform;
+    public final Size size;
+
+    private ViewPortTransform(double width, double height, double rotationAngle, double[][] transform) {
+        this.size = new Size(width, height);
+        this.rotationAngle = rotationAngle;
+        this.transform = transform;
+    }
+
+    public static final class GetViewPortTransformNode extends Node {
+        @Child private CastNode castDoubleVector = newCastBuilder().mustBe(numericValue()).asDoubleVector().buildCastNode();
+        @Child private CastNode castScalarDouble = newCastBuilder().mustBe(numericValue()).asDoubleVector().findFirst().buildCastNode();
+
+        public ViewPortTransform execute(RList viewPort) {
+            double width = Unit.cmToInches(getScalar(viewPort.getDataAt(ViewPort.PVP_WIDTHCM)));
+            double height = Unit.cmToInches(getScalar(viewPort.getDataAt(ViewPort.PVP_HEIGHTCM)));
+            double rotationAngle = getScalar(viewPort.getDataAt(ViewPort.VP_ANGLE));
+            RAbstractDoubleVector trans = (RAbstractDoubleVector) castDoubleVector.execute(viewPort.getDataAt(ViewPort.PVP_TRANS));
+            double[][] transform = TransformMatrix.fromFlat(trans.materialize().getDataWithoutCopying());
+            return new ViewPortTransform(width, height, rotationAngle, transform);
+        }
+
+        private double getScalar(Object value) {
+            return (double) castScalarDouble.execute(value);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..facac095e99e7217f318bd2110c645f0d965b2b9
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
@@ -0,0 +1,48 @@
+/*
+ * 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.library.fastrGrid.device;
+
+/**
+ * Defines parameters for drawing, like color, line style etc.
+ */
+public interface DrawingContext {
+    double INCH_TO_POINTS_FACTOR = 72;
+
+    /**
+     * @return Hexadecimal string of the color with leading '#', e.g. '#FFA8B2'. Never returns a
+     *         synonym.
+     */
+    String getColor();
+
+    /**
+     * Gets the font size in points.
+     *
+     * @see #INCH_TO_POINTS_FACTOR
+     */
+    double getFontSize();
+
+    /**
+     * Gets the height of a line in multiplies of the base line height.
+     */
+    double getLineHeight();
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..278e25ff3d6d6b287652bf18bec551e0a90e492b
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
@@ -0,0 +1,69 @@
+/*
+ * 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.library.fastrGrid.device;
+
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+
+/**
+ * Abstract device that can draw primitive shapes and text. All sizes and coordinates are specified
+ * in inches.
+ */
+public interface GridDevice {
+    void openNewPage();
+
+    void drawRect(DrawingContext ctx, double leftX, double topY, double heigh, double width);
+
+    void drawPolyLines(DrawingContext ctx, double[] x, double[] y, int startIndex, int length);
+
+    void drawString(DrawingContext ctx, double x, double y, double rotation, String text);
+
+    /**
+     * @return The width of the device in inches.
+     */
+    double getWidth();
+
+    /**
+     * @return The height of the device in inches.
+     */
+    double getHeight();
+
+    /**
+     * May change the default values the of the initial drawing context instance.
+     * 
+     * @param ctx instance of drawing context to be altered.
+     */
+    default void initDrawingContext(DrawingContext ctx) {
+        // nop
+    }
+
+    double getStringWidth(DrawingContext ctx, String text);
+
+    /**
+     * Gets the height of a line of text in inches, the default implementation uses only the
+     * parameters from the drawing context, but we allow the device to override this calculation
+     * with something more precise.
+     */
+    default double getStringHeight(DrawingContext ctx, String text) {
+        return (ctx.getLineHeight() * ctx.getFontSize()) / INCH_TO_POINTS_FACTOR;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..aee82b61324b09871f059c925bb425f264f84d84
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
@@ -0,0 +1,132 @@
+/*
+ * 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.library.fastrGrid.device;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+import java.util.function.Supplier;
+
+import com.oracle.truffle.r.library.graphics.FastRFrame;
+
+public class JFrameDevice implements GridDevice {
+    // Grid's coordinate system has origin in left bottom corner and y axis grows from bottom to
+    // top. Moreover, the grid system uses inches as units. We use transformation to adjust the java
+    // coordinate system to the grid one. However, in the case of text rendering, we cannot simply
+    // turn upside down the y-axis, because the text would be upside down too, so for text rendering
+    // only, we reset the transformation completely and transform the coordinates ourselves
+
+    private FastRFrame currentFrame;
+    private Graphics2D graphics;
+
+    @Override
+    public void openNewPage() {
+        if (currentFrame == null) {
+            currentFrame = new FastRFrame();
+            currentFrame.setVisible(true);
+            graphics = (Graphics2D) currentFrame.getGraphics();
+            graphics.translate(0, currentFrame.getHeight());
+            graphics.scale(72, -72); // doc: 72 points ~ 1 inch
+            graphics.setStroke(new BasicStroke(1f / 72f));
+        } else {
+            noTranform(() -> {
+                graphics.clearRect(0, 0, currentFrame.getWidth(), currentFrame.getHeight());
+                return null;
+            });
+        }
+    }
+
+    @Override
+    public void drawRect(DrawingContext ctx, double leftX, double topY, double heigh, double width) {
+        setContext(ctx);
+        graphics.draw(new Rectangle2D.Double(leftX, topY, heigh, width));
+    }
+
+    @Override
+    public void drawPolyLines(DrawingContext ctx, double[] x, double[] y, int startIndex, int length) {
+        assert startIndex >= 0 && startIndex < x.length && startIndex < y.length : "startIndex out of bounds";
+        assert length > 0 && (startIndex + length) <= Math.min(x.length, y.length) : "length out of bounds";
+        setContext(ctx);
+        Path2D.Double path = new Path2D.Double();
+        path.moveTo(x[startIndex], y[startIndex]);
+        for (int i = startIndex + 1; i < length; i++) {
+            path.lineTo(x[i], y[i]);
+        }
+        graphics.draw(path);
+    }
+
+    @Override
+    public void drawString(DrawingContext ctx, double x, double y, double rotation, String text) {
+        setContext(ctx);
+        noTranform(() -> {
+            graphics.rotate(rotation);
+            graphics.drawString(text, (float) x * 72f, (float) (currentFrame.getContentPane().getHeight() - y * 72f));
+            return null;
+        });
+    }
+
+    @Override
+    public double getWidth() {
+        return currentFrame.getContentPane().getWidth() / 72.0;
+    }
+
+    @Override
+    public double getHeight() {
+        return currentFrame.getContentPane().getHeight() / 72.0;
+    }
+
+    @Override
+    public double getStringWidth(DrawingContext ctx, String text) {
+        setContext(ctx);
+        return noTranform(() -> {
+            int swingUnits = graphics.getFontMetrics(graphics.getFont()).stringWidth(text);
+            return swingUnits / 72.;
+        });
+    }
+
+    @Override
+    public double getStringHeight(DrawingContext ctx, String text) {
+        setContext(ctx);
+        return noTranform(() -> {
+            // int swingUnits = graphics.getFontMetrics(graphics.getFont()).getHeight();
+            int swingUnits = graphics.getFont().getSize();
+            return swingUnits / 72.;
+        });
+    }
+
+    private void setContext(DrawingContext ctx) {
+        graphics.setFont(graphics.getFont().deriveFont((float) ctx.getFontSize()));
+        graphics.setColor(Color.decode(ctx.getColor()));
+    }
+
+    private <T> T noTranform(Supplier<T> action) {
+        AffineTransform transform = graphics.getTransform();
+        graphics.setTransform(new AffineTransform());
+        T result = action.get();
+        graphics.setTransform(transform);
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/fastrGrid.R b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/fastrGrid.R
new file mode 100644
index 0000000000000000000000000000000000000000..0dae5cec374614df4108565d33dd8160a94ae2ee
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/fastrGrid.R
@@ -0,0 +1,46 @@
+
+# Returns list with elements [[1]] - depth, zero if not found, [[2]] - the viewport, NULL if not found
+find.viewport <- function(name, strict, pvp, depth) {
+    if (length(ls(env=pvp$children)) == 0) {
+        return(list(FALSE, NULL))
+    } else if (exists(name, env=pvp$children, inherits=FALSE)) {
+        return(list(depth, get(name, env=pvp$children, inherits=FALSE)))
+    } else if (strict) {
+        return(list(FALSE, NULL))
+    } else {
+        return(find.in.children(name, pvp$children, depth + 1L))
+    }
+}
+
+# Note: in GnuR this takes "strict" from find.viewport and forwards it to recursive calls to find.viewport,
+# however, strict must be constant FALSE if find.in.children is called, so we leave it out.
+find.in.children <- function(name, children, depth) {
+  cpvps <- ls(env=children)
+  ncpvp <- length(cpvps)
+  count <- 0L
+  found <- FALSE
+  while (count < ncpvp && !found) {
+    result <- find.viewport(name, FALSE, get(cpvps[count + 1L], env=children), depth)
+    if (result[[1L]]) {
+        return(result);
+    }
+    count <- count + 1L
+  }
+  list(FALSE, NULL) # not found
+}
+
+L_downviewport <- function(name, strict) {
+    currVp <- .Call(grid:::L_currentViewport)
+    result <- find.viewport(name, strict, currVp, 1L);
+    if (result[[1]]) {
+        .Internal(.fastr.grid.doSetViewPort(result[[2L]], FALSE, FALSE));
+        return(result[[1L]])
+    } else {
+        stop(paste0("Viewport '", name, "' was not found"));
+    }
+}
+
+L_setviewport <- function(vp, hasParent) {
+    pushedVP <- grid:::pushedvp(vp);
+    .Internal(.fastr.grid.doSetViewPort(pushedVP, hasParent, TRUE));
+}
\ No newline at end of file
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java
index 9f1463e8549ecc02f5eca556a94f7e22426d590b..7446c48d593045bd08e9a437ff882112bb00806f 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -35,7 +35,7 @@ public class FastRFrame extends JFrame {
 
     private static final long serialVersionUID = 1L;
 
-    private final Dimension framePreferredSize = new Dimension(500, 500);
+    private final Dimension framePreferredSize = new Dimension(720, 720);
     private final FastRComponent fastRComponent = new FastRComponent();
 
     public FastRFrame() throws HeadlessException {
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 16e97d3504be001bfc427e6ff7b491f67f484b17..ac9d5aa0bc70311575eb482b5521b1207fb2f4fa 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
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import java.util.function.Supplier;
 
 import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.r.library.fastrGrid.DoSetViewPortBuiltin;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.binary.BinaryArithmeticNodeGen;
@@ -717,6 +718,9 @@ public class BasePackage extends RBuiltinPackage {
         add(UpdateSubset.class, UpdateSubsetNodeGen::create, UpdateSubset::special);
         add(UpdateField.class, UpdateFieldNodeGen::create, UpdateField::createSpecial);
         add(WhileBuiltin.class, WhileBuiltinNodeGen::create);
+
+        // grid intrinsics
+        add(DoSetViewPortBuiltin.class, DoSetViewPortBuiltin::create);
     }
 
     private void addBinaryArithmetic(Class<?> builtinClass, BinaryArithmeticFactory binaryFactory, UnaryArithmeticFactory unaryFactory) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
index ab28e791fd8d730b36e6b70e815fb86459e08692..29c9cc4d625a3aab0b9371d8c0bb84f3980ddd11 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
@@ -22,6 +22,18 @@ 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.frame.VirtualFrame;
+import com.oracle.truffle.r.library.fastrGrid.GridStateGetNode;
+import com.oracle.truffle.r.library.fastrGrid.GridStateSetNode;
+import com.oracle.truffle.r.library.fastrGrid.IgnoredGridExternal;
+import com.oracle.truffle.r.library.fastrGrid.LGridDirty;
+import com.oracle.truffle.r.library.fastrGrid.LInitGrid;
+import com.oracle.truffle.r.library.fastrGrid.LInitViewPortStack;
+import com.oracle.truffle.r.library.fastrGrid.LLines;
+import com.oracle.truffle.r.library.fastrGrid.LNewPage;
+import com.oracle.truffle.r.library.fastrGrid.LRect;
+import com.oracle.truffle.r.library.fastrGrid.LSegments;
+import com.oracle.truffle.r.library.fastrGrid.LText;
+import com.oracle.truffle.r.library.fastrGrid.LUpViewPort;
 import com.oracle.truffle.r.library.grDevices.DevicesCCalls;
 import com.oracle.truffle.r.library.graphics.GraphicsCCalls;
 import com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_Par;
@@ -79,6 +91,8 @@ import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalCode;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
@@ -240,6 +254,9 @@ public class CallAndExternalFunctions {
         @TruffleBoundary
         protected RExternalBuiltinNode lookupBuiltin(RList symbol) {
             String name = lookupName(symbol);
+            if (FastROptions.UseInternalGridGraphics.getBooleanValue() && name != null && name.startsWith("L_")) {
+                return lookupFastRGridBuiltin(name);
+            }
             switch (name) {
                 // methods
                 case "R_initMethodDispatch":
@@ -652,11 +669,77 @@ public class CallAndExternalFunctions {
             }
         }
 
+        private RExternalBuiltinNode lookupFastRGridBuiltin(String name) {
+            switch (name) {
+                case "L_gridDirty":
+                    return new LGridDirty();
+                case "L_initGrid":
+                    return LInitGrid.create();
+                case "L_newpage":
+                    return new LNewPage();
+
+                // Viewport management
+                case "L_upviewport":
+                    return LUpViewPort.create();
+                case "L_initViewportStack":
+                    return new LInitViewPortStack();
+                case "L_setviewport":
+                case "L_downviewport":
+                    return getExternalFastRGridBuiltinNode(name);
+
+                // Drawing primitives
+                case "L_rect":
+                    return LRect.create();
+                case "L_lines":
+                    return LLines.create();
+                case "L_text":
+                    return LText.create();
+                case "L_segments":
+                    return LSegments.create();
+
+                // Simple grid state access
+                case "L_getGPar":
+                    return new GridStateGetNode(state -> state.getGpar());
+                case "L_setGPar":
+                    return GridStateSetNode.create((state, val) -> state.setGpar((RList) val));
+                case "L_getCurrentGrob":
+                    return new GridStateGetNode(state -> state.getCurrentGrob());
+                case "L_setCurrentGrob":
+                    return GridStateSetNode.create((state, val) -> state.setCurrentGrob(val));
+                case "L_currentViewport":
+                    return new GridStateGetNode(state -> state.getViewPort());
+
+                // Display list stuff: not implemented atm
+                case "L_getDisplayList":
+                    return new IgnoredGridExternal(RDataFactory.createList());
+                case "L_getDLindex":
+                    return new IgnoredGridExternal(0);
+                case "L_getDLon":
+                case "L_getEngineDLon":
+                    return new IgnoredGridExternal(RRuntime.LOGICAL_FALSE);
+                case "L_initDisplayList":
+                case "L_newpagerecording":
+                case "L_setDisplayList":
+                case "L_setDLelt":
+                case "L_setDLindex":
+                case "L_setDLon":
+                    return new IgnoredGridExternal(RNull.instance);
+
+                // These methods do not use graphics system or any global state. For now,
+                // we can re-use the native implementation, which in the future should be rewritten
+                // to managed code.
+                case "L_validUnits":
+                    return null;
+                default:
+                    throw RInternalError.shouldNotReachHere("Unimplemented grid external " + name);
+            }
+        }
+
         /**
          * {@code .NAME = NativeSymbolInfo} implemented as a builtin.
          */
         @SuppressWarnings("unused")
-        @Specialization(limit = "1", guards = {"cached == symbol", "builtin != null"})
+        @Specialization(limit = "99", guards = {"cached == symbol", "builtin != null"})
         protected Object doExternal(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
                         @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin) {
@@ -668,9 +751,10 @@ public class CallAndExternalFunctions {
          * package)
          */
         @SuppressWarnings("unused")
-        @Specialization(limit = "2", guards = {"cached == symbol"})
+        @Specialization(limit = "2", guards = {"cached == symbol", "builtin == null"})
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
+                        @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin,
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
@@ -680,8 +764,12 @@ public class CallAndExternalFunctions {
          * such cases there is this generic version.
          */
         @SuppressWarnings("unused")
-        @Specialization(replaces = "callNamedFunction")
+        @Specialization(replaces = {"callNamedFunction", "doExternal"})
         protected Object callNamedFunctionGeneric(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName) {
+            RExternalBuiltinNode builtin = lookupBuiltin(symbol);
+            if (builtin != null) {
+                throw RInternalError.shouldNotReachHere("Cache for .Calls with FastR reimplementation (lookupBuiltin(...) != null) exceeded the limit");
+            }
             NativeCallInfo nativeCallInfo = extractSymbolInfo(frame, symbol);
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java
index 31f230f5226efbc9c0c40a1c00d8991adbb69e06..84c0278f50d87e448f4aa0c579bebbd3fd147357 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.LInitGrid;
 import com.oracle.truffle.r.library.stats.RandFunctionsNodes;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
@@ -177,6 +178,10 @@ abstract class LookupAdapter extends RBuiltinNode {
         return new RInternalCodeBuiltinNode(RContext.getInstance(), "stats", RInternalCode.loadSourceRelativeTo(RandFunctionsNodes.class, "model.R"), name);
     }
 
+    protected static RExternalBuiltinNode getExternalFastRGridBuiltinNode(String name) {
+        return new RInternalCodeBuiltinNode(RContext.getInstance(), "grid", RInternalCode.loadSourceRelativeTo(LInitGrid.class, "fastrGrid.R"), name);
+    }
+
     protected static final int CallNST = DLL.NativeSymbolType.Call.ordinal();
     protected static final int ExternalNST = DLL.NativeSymbolType.External.ordinal();
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index 925e1f39042c3510b80c4d18eb2f1cdbcb18bd93..157526ea1d32140397e0b470186f2e6e8dc78574 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -52,6 +52,7 @@ public enum FastROptions {
     InvisibleArgs("Argument writes do not trigger state transitions", true),
     RefCountIncrementOnly("Disable reference count decrements for experimental state transition implementation", false),
     UseInternalGraphics("Whether the internal (Java) graphics subsystem should be used", false),
+    UseInternalGridGraphics("Whether the internal (Java) grid graphics implementation should be used", false),
     UseSpecials("Whether the fast-path special call nodes should be created for simple enough arguments.", true),
     ForceSources("Generate source sections for unserialized code", false),
 
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index f6786e5110ab80a04fbc540258a0b5e20b71bfcc..b9a4e7de5c3a1b693b7eb5ced22a2739f83b77e9 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -766,3 +766,24 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/p
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PairListPrinter.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/deriv/Deriv.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/deriv/DerivVisitor.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ColorNames.java,gnu_r.copyright