From eb55b547993edc5384292eabd51bdb142754bd15 Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Tue, 3 Mar 2015 18:07:54 -0800
Subject: [PATCH] All parsing now uses Source objects. Rewrite parse builtin to
 do best effort Source creation and also create standard srcref attribute

---
 .../com/oracle/truffle/r/engine/REngine.java  |  16 +-
 .../builtin/base/ConnectionFunctions.java     |   3 +-
 .../truffle/r/nodes/builtin/base/Lapply.java  |  10 +-
 .../truffle/r/nodes/builtin/base/Parse.java   | 208 +++++++++++++++---
 .../truffle/r/nodes/RTruffleVisitor.java      |   4 +-
 .../truffle/r/nodes/control/BreakNode.java    |   3 -
 .../truffle/r/nodes/control/NextNode.java     |   3 -
 .../r/nodes/instrument/RNodeTimer.java        |  16 +-
 .../r/nodes/runtime/RASTHelperImpl.java       |   3 +-
 .../oracle/truffle/r/runtime/RContext.java    |   9 +-
 .../oracle/truffle/r/runtime/RSerialize.java  |   4 +-
 .../r/runtime/conn/ConnectionSupport.java     |  10 +
 12 files changed, 219 insertions(+), 70 deletions(-)

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index ce6a417c4d..30068d4419 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -34,7 +34,6 @@ import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.library.graphics.*;
 import com.oracle.truffle.r.nodes.*;
@@ -210,18 +209,9 @@ public final class REngine implements RContext.Engine {
         }
     }
 
-    public Node parseSingle(String singleExpression) {
+    public RExpression parse(Source source) throws RContext.Engine.ParseException {
         try {
-            Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(singleExpression), Source.asPseudoFile(singleExpression, "<parse_input>"));
-            return transform(seq.getExpressions()[0]);
-        } catch (RecognitionException ex) {
-            throw Utils.fatalError("parseSingle failed");
-        }
-    }
-
-    public RExpression parse(String rscript) throws RContext.Engine.ParseException {
-        try {
-            Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(rscript), Source.asPseudoFile(rscript, "<parse_input>"));
+            Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source);
             ASTNode[] exprs = seq.getExpressions();
             Object[] data = new Object[exprs.length];
             for (int i = 0; i < exprs.length; i++) {
@@ -463,7 +453,7 @@ public final class REngine implements RContext.Engine {
     }
 
     // Only relevant when running without base package loaded
-    private static final String INTERNAL_PRINT = ".print.internal <- function(x) { .Internal(print.default(x, NULL, TRUE, NULL, NULL, FALSE, NULL, TRUE))" + "}";
+    private static final Source INTERNAL_PRINT = Source.asPseudoFile(".print.internal <- function(x) { .Internal(print.default(x, NULL, TRUE, NULL, NULL, FALSE, NULL, TRUE))}", "<internal_print>");
     @CompilationFinal private static RFunction printInternal;
 
     private static RFunction getPrintInternal() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
index 11cddcbd99..0a63fefd31 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
@@ -96,8 +96,9 @@ public abstract class ConnectionFunctions {
             if (!RRuntime.fromLogical(blocking)) {
                 throw RError.nyi(getEncapsulatingSourceSection(), " non-blocking mode not supported");
             }
+            String path = removeFileURLPrefix(description.getDataAt(0));
             try {
-                return new FileRConnection(description.getDataAt(0), open.getDataAt(0));
+                return new FileRConnection(path, open.getDataAt(0));
             } catch (IOException ex) {
                 RError.warning(RError.Message.CANNOT_OPEN_FILE, description.getDataAt(0), ex.getMessage());
                 throw RError.error(getEncapsulatingSourceSection(), RError.Message.CANNOT_OPEN_CONNECTION);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
index 3a31e7bab6..a05546ed26 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
@@ -20,6 +20,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.array.read.*;
@@ -30,6 +31,7 @@ import com.oracle.truffle.r.nodes.builtin.base.Lapply.GeneralLApplyNode.LapplyIt
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.nodes.function.MatchedArguments.MatchedArgumentsNode;
 import com.oracle.truffle.r.runtime.*;
+import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
@@ -115,6 +117,7 @@ public abstract class Lapply extends RBuiltinNode {
          */
         protected static class LapplyIteratorNode extends RNode {
             private static final String ITER_INDEX_NAME = AnonymousFrameVariable.create("LAPPLY_ITER_INDEX");
+            private static final Source ACCESS_ARRAY_SOURCE = Source.asPseudoFile("X[[i]]", "<lapply_array_access>");
 
             /**
              * Increments the iterator index, updating the {@link #ITER_INDEX_NAME} variable. Always
@@ -147,7 +150,12 @@ public abstract class Lapply extends RBuiltinNode {
 
             RNode getIndexedLoad() {
                 if (indexedLoad == null) {
-                    AccessArrayNode indexNode = (AccessArrayNode) RContext.getEngine().parseSingle("X[[i]]");
+                    AccessArrayNode indexNode;
+                    try {
+                        indexNode = (AccessArrayNode) ((RLanguage) RContext.getEngine().parse(ACCESS_ARRAY_SOURCE).getDataAt(0)).getRep();
+                    } catch (ParseException ex) {
+                        throw RInternalError.shouldNotReachHere();
+                    }
                     REnvironment env = RDataFactory.createNewEnv("dummy");
                     env.safePut("i", RDataFactory.createLanguage(ReadVariableNode.create(ITER_INDEX_NAME, false)));
                     indexedLoad = indexNode.substitute(env);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index 606e346ad2..42ca5dd6dd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -26,14 +26,20 @@ import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
 import java.io.*;
 
+import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.builtin.*;
+import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RContext.Engine.ParseException;
 import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
+import com.oracle.truffle.r.runtime.env.*;
 
 /**
  * Internal component of the {@code parse} base package function.
@@ -43,55 +49,111 @@ import com.oracle.truffle.r.runtime.data.model.*;
  * </pre>
  *
  * TODO handle case when {@code srcFile != NULL};
+ *
+ * There are two main modalities in the arguments:
+ * <ul>
+ * <li>Input is taken from "conn" or "text" (in which case conn==stdin(), but ignored).</li>
+ * <li>Parse the entire input or just "n" "expressions". The FastR parser cannot handle the latter
+ * case properly. It will parse the entire stream whereas GnuR stops after "n" expressions. So,
+ * e.g., if there is a syntax error after the "n'th" expression, GnuR does not see it, whereas FastR
+ * does and throws an error. However, if there is no error FastR can truncate the expressions vector
+ * to length "n"</li>
+ * </ul>
+ * Despite the modality there is no value in multiple specializations for what is an inherently
+ * slow-path builtin.
+ * <p>
+ * The inputs do not lend themselves to the correct creation of {@link Source} attributes for the
+ * FastR AST. In particular the {@code source} builtin reads the input internally and calls us the
+ * "text" variant. However useful information regarding the origin of the input can be found either
+ * in the connection info or in the "srcfile" argument which, if not {@code RNull#instance} is an
+ * {@link REnvironment} with relevant data. So we can fix up the {@link Source} attributes on the
+ * AST after the parse. It's relevant to do this for the Truffle instrumentation framework.
+ * <p>
+ * On the R side, GnuR adds similar R attributes to the result, which is important for R tooling.
  */
 @RBuiltin(name = "parse", kind = INTERNAL, parameterNames = {"conn", "n", "text", "prompt", "srcfile", "encoding"})
 public abstract class Parse extends RBuiltinNode {
+    @Child private CastIntegerNode castIntNode;
+    @Child private CastStringNode castStringNode;
+    @Child private CastToVectorNode castVectorNode;
 
-    @SuppressWarnings("unused")
-    @Specialization
-    protected Object parse(RConnection conn, RNull n, RNull text, String prompt, Object srcFile, String encoding) {
-        controlVisibility();
-        try {
-            String[] lines = conn.readLines(0);
-            return doParse(coalesce(lines));
-        } catch (IOException | ParseException ex) {
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.PARSE_ERROR);
+    private int castInt(VirtualFrame frame, Object n) {
+        if (castIntNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castIntNode = insert(CastIntegerNodeGen.create(null, false, false, false));
+        }
+        int result = (int) castIntNode.executeInt(frame, n);
+        if (RRuntime.isNA(result)) {
+            result = -1;
         }
+        return result;
+    }
+
+    private RStringVector castString(VirtualFrame frame, Object s) {
+        if (castStringNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            castVectorNode = insert(CastToVectorNodeGen.create(null, false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(null, false, false, false, false));
+        }
+        return (RStringVector) castStringNode.executeString(frame, castVectorNode.executeObject(frame, s));
     }
 
-    @SuppressWarnings("unused")
     @Specialization
-    protected Object parse(RConnection conn, double n, RNull text, String prompt, Object srcFile, String encoding) {
+    protected Object parse(VirtualFrame frame, RConnection conn, Object n, Object text, RAbstractStringVector prompt, Object srcFile, RAbstractStringVector encoding) {
         controlVisibility();
-        try {
-            String[] lines = conn.readLines((int) n);
-            return doParse(coalesce(lines));
-        } catch (IOException | ParseException ex) {
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.PARSE_ERROR);
+        int nAsInt;
+        if (n != RNull.instance) {
+            nAsInt = castInt(frame, n);
+        } else {
+            nAsInt = -1;
+        }
+        Object textVec = text;
+        if (textVec != RNull.instance) {
+            textVec = castString(frame, textVec);
         }
+        return doParse(conn, nAsInt, textVec, prompt, srcFile, encoding);
     }
 
+    @TruffleBoundary
     @SuppressWarnings("unused")
-    @Specialization(guards = "parseEntire")
-    protected Object parse(RConnection conn, Object n, RAbstractStringVector textVec, String prompt, Object srcFile, String encoding) {
-        controlVisibility();
-        String text = coalesce(((RStringVector) textVec).getDataWithoutCopying());
-        if (text.length() == 0) {
+    private Object doParse(RConnection conn, int n, Object textVec, RAbstractStringVector prompt, Object srcFile, RAbstractStringVector encoding) {
+        String[] lines;
+        if (textVec == RNull.instance) {
+            if (conn == StdConnections.getStdin()) {
+                throw RError.nyi(getEncapsulatingSourceSection(), " parse from stdin not implemented");
+            }
+            try (RConnection openConn = conn.forceOpen("r")) {
+                lines = openConn.readLines(0);
+            } catch (IOException ex) {
+                throw RError.error(getEncapsulatingSourceSection(), RError.Message.PARSE_ERROR);
+            }
+        } else {
+            lines = ((RStringVector) textVec).getDataWithoutCopying();
+        }
+        String coalescedLines = coalesce(lines);
+        if (coalescedLines.length() == 0 || n == 0) {
             return RDataFactory.createExpression(RDataFactory.createList());
         }
         try {
-            return doParse(text);
+            Source source = srcFile != RNull.instance ? createSource(srcFile, coalescedLines) : createSource(conn);
+            RExpression exprs = RContext.getEngine().parse(source);
+            if (n > 0 && n > exprs.getLength()) {
+                RList list = exprs.getList();
+                Object[] listData = list.getDataCopy();
+                Object[] subListData = new Object[n];
+                System.arraycopy(listData, 0, subListData, 0, n);
+                exprs = RDataFactory.createExpression(RDataFactory.createList(subListData));
+            }
+            // Handle the required R attributes
+            if (srcFile instanceof REnvironment) {
+                addAttributes(exprs, source, (REnvironment) srcFile);
+            }
+            return exprs;
         } catch (ParseException ex) {
             throw RError.error(getEncapsulatingSourceSection(), RError.Message.PARSE_ERROR);
         }
     }
 
-    @TruffleBoundary
-    private static RExpression doParse(String script) throws ParseException {
-        return RContext.getEngine().parse(script);
-    }
-
-    @TruffleBoundary
     private static String coalesce(String[] lines) {
         StringBuffer sb = new StringBuffer();
         for (String line : lines) {
@@ -101,16 +163,90 @@ public abstract class Parse extends RBuiltinNode {
         return sb.toString();
     }
 
-    public static boolean parseEntire(@SuppressWarnings("unused") Object conn, Object n) {
-        if (n == RNull.instance) {
-            return true;
-        } else if (n instanceof Double && (((Double) n == -1 || ((Double) n) == RRuntime.DOUBLE_NA))) {
-            return true;
-        } else if (n instanceof Integer && (((Integer) n == -1 || ((Integer) n) == RRuntime.INT_NA))) {
-            return true;
+    /**
+     * Creates a {@link Source} object by gleaning information from {@code srcFile}.
+     */
+    private static Source createSource(Object srcFile, String coalescedLines) {
+        if (srcFile instanceof REnvironment) {
+            REnvironment srcFileEnv = (REnvironment) srcFile;
+            boolean isFile = RRuntime.fromLogical((byte) srcFileEnv.get("isFile"));
+            if (isFile) {
+                // Might be a URL
+                String fileName = ConnectionSupport.removeFileURLPrefix((String) srcFileEnv.get("filename"));
+                File fnf = new File(fileName);
+                String path = null;
+                if (!fnf.isAbsolute()) {
+                    String wd = RRuntime.asString(srcFileEnv.get("wd"));
+                    path = String.join(File.separator, wd, fileName);
+                } else {
+                    path = fileName;
+                }
+                return createFileSource(path);
+            } else {
+                return Source.asPseudoFile(coalescedLines, "<parse>");
+            }
         } else {
-            return false;
+            String srcFileText = RRuntime.asString(srcFile);
+            if (srcFileText.equals("<text>")) {
+                return Source.asPseudoFile(coalescedLines, "<parse>");
+            } else {
+                return createFileSource(ConnectionSupport.removeFileURLPrefix(srcFileText));
+            }
         }
+
+    }
+
+    private static Source createSource(RConnection conn) {
+        // TODO check if file
+        String path = ConnectionSupport.getBaseConnection(conn).getSummaryDescription();
+        return createFileSource(path);
+    }
+
+    private static Source createFileSource(String path) {
+        try {
+            /*
+             * Although we have read the source, it will be re-read in parse as there is currently
+             * no way to pass the source we have to Source.fromFileName.
+             */
+            return Source.fromFileName(path);
+        } catch (IOException ex) {
+            throw RInternalError.shouldNotReachHere();
+        }
+    }
+
+    private static void addAttributes(RExpression exprs, Source source, REnvironment srcFile) {
+        Object[] srcrefData = new Object[exprs.getLength()];
+        for (int i = 0; i < srcrefData.length; i++) {
+            Node node = (Node) ((RLanguage) exprs.getDataAt(i)).getRep();
+            SourceSection ss = node.getSourceSection();
+            int[] llocData = new int[8];
+            int startLine = ss.getStartLine();
+            int startColumn = ss.getStartColumn();
+            int lastLine = source.getLineNumber(ss.getCharEndIndex());
+            int lastColumn = source.getColumnNumber(ss.getCharEndIndex()) - 1;
+            // no multi-byte support, so byte==line
+            llocData[0] = startLine;
+            llocData[1] = startColumn;
+            llocData[2] = lastLine;
+            llocData[3] = lastColumn;
+            llocData[4] = startColumn;
+            llocData[5] = lastColumn;
+            llocData[6] = startLine;
+            llocData[7] = lastLine;
+            RIntVector lloc = RDataFactory.createIntVector(llocData, RDataFactory.COMPLETE_VECTOR);
+            srcrefData[i] = lloc;
+        }
+        exprs.setAttr("srcref", RDataFactory.createList(srcrefData));
+        int[] wholeSrcrefData = new int[8];
+        int endOffset = source.getCode().length() - 1;
+        wholeSrcrefData[0] = source.getLineNumber(0);
+        wholeSrcrefData[3] = source.getLineNumber(endOffset);
+        source.getColumnNumber(0);
+        wholeSrcrefData[6] = wholeSrcrefData[0];
+        wholeSrcrefData[6] = wholeSrcrefData[3];
+
+        exprs.setAttr("wholeSrcref", RDataFactory.createIntVector(wholeSrcrefData, RDataFactory.COMPLETE_VECTOR));
+        exprs.setAttr("srcfile", srcFile);
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
index f1b7a1ec4f..7620a280d3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
@@ -630,12 +630,12 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
 
     @Override
     public RNode visit(Break n) {
-        return new BreakNode();
+        return new BreakNode(n.getSource());
     }
 
     @Override
     public RNode visit(Next n) {
-        return new NextNode();
+        return new NextNode(n.getSource());
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
index 44a00eaf1d..6adc03edc2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BreakNode.java
@@ -29,9 +29,6 @@ import com.oracle.truffle.r.runtime.RDeparse.*;
 
 public final class BreakNode extends RNode {
 
-    public BreakNode() {
-    }
-
     public BreakNode(SourceSection src) {
         assignSourceSection(src);
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
index d6e940632d..71473aef1a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/NextNode.java
@@ -29,9 +29,6 @@ import com.oracle.truffle.r.runtime.RDeparse.State;
 
 public final class NextNode extends RNode {
 
-    public NextNode() {
-    }
-
     public NextNode(SourceSection src) {
         assignSourceSection(src);
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RNodeTimer.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RNodeTimer.java
index 92eedfa28b..72de66a845 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RNodeTimer.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RNodeTimer.java
@@ -28,7 +28,9 @@ import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.runtime.*;
 
 /**
@@ -128,7 +130,19 @@ public class RNodeTimer {
                 data.toArray(sortedData);
                 Arrays.sort(sortedData);
                 for (StatementData sd : sortedData) {
-                    System.out.printf("%10d: %s%n", sd.time, sd.node.getSourceSection().getCode());
+                    System.out.printf("%10d: %s%n", sd.time, safeGetCode(sd.node));
+                }
+            }
+
+            private static String safeGetCode(Node node) {
+                SourceSection ss = node.getSourceSection();
+                if (ss != null) {
+                    return ss.getCode();
+                } else {
+                    FunctionDefinitionNode rootNode = (FunctionDefinitionNode) node.getRootNode();
+                    RDeparse.State state = RDeparse.State.createPrintableState();
+                    rootNode.deparse(state);
+                    return state.toString();
                 }
             }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java
index 97fb50c3a7..ba50c1f4a1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/runtime/RASTHelperImpl.java
@@ -256,6 +256,7 @@ public class RASTHelperImpl implements RASTHelper {
         RASTDeparse.deparse(state, f);
     }
 
+    private static final Source GET_NAMESPACE_SOURCE = Source.asPseudoFile("..getNamespace(name)", "<..getNamespace>");
     private static RCallNode getNamespaceCall;
 
     /**
@@ -265,7 +266,7 @@ public class RASTHelperImpl implements RASTHelper {
     public REnvironment findNamespace(RStringVector name, int depth) {
         if (getNamespaceCall == null) {
             try {
-                getNamespaceCall = (RCallNode) ((RLanguage) RContext.getEngine().parse("..getNamespace(name)").getDataAt(0)).getRep();
+                getNamespaceCall = (RCallNode) ((RLanguage) RContext.getEngine().parse(GET_NAMESPACE_SOURCE).getDataAt(0)).getRep();
             } catch (ParseException ex) {
                 // most unexpected
                 Utils.fail("findNameSpace");
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
index 73869dd34c..e0bcf80988 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java
@@ -28,7 +28,6 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
@@ -161,13 +160,7 @@ public final class RContext extends ExecutionContext {
          * Parse an R expression and return an {@link RExpression} object representing the Truffle
          * ASTs for the components.
          */
-        RExpression parse(String rscript) throws ParseException;
-
-        /**
-         * Evaluates a single R expression and returns the AST node for it, For internal use. Parse
-         * errors are fatal.
-         */
-        Node parseSingle(String singleExpression);
+        RExpression parse(Source source) throws ParseException;
 
         /**
          * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index 2f1fed3cb6..4faf59805f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -15,6 +15,7 @@ import java.io.*;
 import java.util.*;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.source.*;
 import com.oracle.truffle.r.options.*;
 import com.oracle.truffle.r.runtime.conn.*;
 import com.oracle.truffle.r.runtime.data.*;
@@ -358,7 +359,8 @@ public class RSerialize {
                              * there (and overwrite the promise), so we fix the enclosing frame up
                              * on return.
                              */
-                            RExpression expr = RContext.getEngine().parse(deparse);
+                            Source source = Source.asPseudoFile(deparse, "<package deparse>");
+                            RExpression expr = RContext.getEngine().parse(source);
                             RFunction func = (RFunction) RContext.getEngine().eval(expr, RDataFactory.createNewEnv(REnvironment.emptyEnv(), 0), depth + 1);
                             func.setEnclosingFrame(((REnvironment) rpl.getTag()).getFrame());
                             result = func;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
index ad61813fff..3c780a2d48 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java
@@ -181,6 +181,16 @@ public class ConnectionSupport {
 
     }
 
+    public static final String FILE_URL_PREFIX = "file://";
+
+    public static String removeFileURLPrefix(String path) {
+        if (path.startsWith(FILE_URL_PREFIX)) {
+            return path.replace(FILE_URL_PREFIX, "");
+        } else {
+            return path;
+        }
+    }
+
     // TODO implement all open modes
 
     /**
-- 
GitLab