From 155f8b3c082614380116e86e9e371aaec93fe47a Mon Sep 17 00:00:00 2001
From: Florian Angerer <florian.angerer@oracle.com>
Date: Wed, 31 May 2017 15:32:43 +0200
Subject: [PATCH] Implemented srcref for block statements.

---
 .../oracle/truffle/r/runtime/RSerialize.java  | 55 +++++++----
 .../com/oracle/truffle/r/runtime/RSrcref.java | 94 ++++++++++++++++++-
 2 files changed, 130 insertions(+), 19 deletions(-)

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 63106513ce..0ff77a7e35 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
@@ -1759,11 +1759,13 @@ public class RSerialize {
             if (outAttrs != null) {
                 SourceSection ss = outAttrs.getSourceReferenceAttributes();
                 if (ss != null) {
-                    String path = ss.getSource().getURI().getPath();
-                    REnvironment createSrcfile = RSrcref.createSrcfile(path);
-                    Object createLloc = RSrcref.createLloc(ss, createSrcfile);
-                    writePairListEntry(RRuntime.R_SRCREF, createLloc);
-                    writePairListEntry(RRuntime.R_SRCFILE, createSrcfile);
+                    String path = RSource.getPathInternal(ss.getSource());
+                    if (path != null) {
+                        REnvironment createSrcfile = RSrcref.createSrcfile(path);
+                        Object createLloc = RSrcref.createLloc(ss, createSrcfile);
+                        writePairListEntry(RRuntime.R_SRCREF, createLloc);
+                        writePairListEntry(RRuntime.R_SRCFILE, createSrcfile);
+                    }
                 }
                 DynamicObject attributes = outAttrs.getExplicitAttributes();
                 if (attributes != null) {
@@ -2094,13 +2096,7 @@ public class RSerialize {
         private DynamicObject explicitAttributes;
         private SourceSection ss;
 
-        OutAttributes(RAttributable obj, SEXPTYPE type) {
-
-            explicitAttributes = obj.getAttributes();
-            initSourceSection(obj, type);
-        }
-
-        OutAttributes(Object obj, SEXPTYPE type) {
+        private OutAttributes(Object obj, SEXPTYPE type) {
 
             if (obj instanceof RAttributable) {
                 explicitAttributes = ((RAttributable) obj).getAttributes();
@@ -2112,10 +2108,14 @@ public class RSerialize {
             if (type == SEXPTYPE.FUNSXP) {
                 RFunction fun = (RFunction) obj;
                 RSyntaxFunction body = (RSyntaxFunction) fun.getRootNode();
-                SourceSection lazySourceSection = body.getLazySourceSection();
-                if (lazySourceSection != RSyntaxNode.LAZY_DEPARSE) {
-                    ss = lazySourceSection;
-                }
+                setSourceSection(body);
+            }
+        }
+
+        private void setSourceSection(RSyntaxElement body) {
+            SourceSection lazySourceSection = body.getLazySourceSection();
+            if (lazySourceSection != RSyntaxNode.LAZY_DEPARSE) {
+                ss = lazySourceSection;
             }
         }
 
@@ -2315,7 +2315,6 @@ public class RSerialize {
 
         @Override
         protected Void visit(RSyntaxLookup element) {
-// setSrcrefs(element)
             state.setCarAsSymbol(element.getIdentifier());
             return null;
         }
@@ -2410,7 +2409,27 @@ public class RSerialize {
     private static void serializeFunctionDefinition(State state, RSyntaxFunction function) {
         SerializeVisitor visitor = new SerializeVisitor(state);
         state.setCar(visitor.visitFunctionFormals(function));
-        state.setCdr(visitor.visitFunctionBody(function));
+        Object visitFunctionBody = visitor.visitFunctionBody(function);
+
+        // convert and attach source section to srcref attribute
+        if (visitFunctionBody instanceof RAttributable) {
+
+            SourceSection lazySourceSection = function.getLazySourceSection();
+            if (lazySourceSection != null) {
+                String pathInternal = RSource.getPathInternal(lazySourceSection.getSource());
+                if (pathInternal != null) {
+                    RAttributable visitFunctionBody2 = (RAttributable) visitFunctionBody;
+                    RList createBlockSrcrefs = RSrcref.createBlockSrcrefs(function);
+                    if (createBlockSrcrefs != null) {
+                        visitFunctionBody2.setAttr(RRuntime.R_SRCREF, createBlockSrcrefs);
+                        visitFunctionBody2.setAttr(RRuntime.R_WHOLE_SRCREF, RSrcref.createLloc(lazySourceSection));
+                        visitFunctionBody2.setAttr(RRuntime.R_SRCFILE, RSrcref.createSrcfile(pathInternal));
+                    }
+                }
+            }
+        }
+
+        state.setCdr(visitFunctionBody);
     }
 
     private static Object serializeLanguage(State state, RLanguage lang) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java
index 247bf965ad..f107a449ad 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java
@@ -18,6 +18,8 @@ import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.PosixFileAttributes;
+import java.util.LinkedList;
+import java.util.List;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.source.Source;
@@ -25,6 +27,7 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
+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.RAbstractIntVector;
@@ -32,7 +35,13 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
 
 /**
  * Utilities for handling R srcref attributes, in particular conversion from {@link Source},
@@ -103,6 +112,88 @@ public class RSrcref {
         return createLloc(ss, createSrcfile(path));
     }
 
+    /**
+     * Creates a block source reference or {@code null} if the function's body is not a block
+     * statement.<br>
+     * Srcref for blocks are different in that it is an RList of srcref vectors whereas each element
+     * corresponds to one syntax call in the block (including the block itself). E.g.
+     * <p>
+     * <code> {<br/>
+     * print('Hello')<br/>
+     * print(x)<br/>
+     * }</code>
+     * </p>
+     * will result in [[1, 20, 4, 1, 20, 1, 1, 4], [2, 2, 2, 15, 2, 15, 2, 2], [3, 2, 3, 9, 2, 9, 3,
+     * 3]]
+     *
+     * @param function
+     */
+    @TruffleBoundary
+    public static RList createBlockSrcrefs(RSyntaxFunction function) {
+
+        List<Object> blockSrcrefs = new LinkedList<>();
+
+        RSyntaxVisitor<Void> v = new RSyntaxVisitor<Void>() {
+
+            private int depth = 0;
+
+            @Override
+            public Void visit(RSyntaxCall element) {
+
+                if (depth == 0 && !isBlockStatement(element)) {
+                    return null;
+                }
+
+                SourceSection lazySourceSection = element.getLazySourceSection();
+                if (lazySourceSection != null) {
+                    blockSrcrefs.add(createLloc(lazySourceSection));
+                }
+
+                if (depth == 0) {
+                    RSyntaxElement[] syntaxArguments = element.getSyntaxArguments();
+                    for (int i = 0; i < syntaxArguments.length; i++) {
+                        if (syntaxArguments[i] != null) {
+                            depth++;
+                            accept(syntaxArguments[i]);
+                            depth--;
+                        }
+                    }
+                }
+                return null;
+            }
+
+            private boolean isBlockStatement(RSyntaxCall element) {
+                RSyntaxElement lhs = element.getSyntaxLHS();
+                if (lhs instanceof RSyntaxLookup) {
+                    return "{".equals(((RSyntaxLookup) lhs).getIdentifier());
+                }
+                return false;
+            }
+
+            @Override
+            public Void visit(RSyntaxConstant element) {
+                return null;
+            }
+
+            @Override
+            public Void visit(RSyntaxLookup element) {
+                return null;
+            }
+
+            @Override
+            public Void visit(RSyntaxFunction element) {
+                accept(element.getSyntaxBody());
+                return null;
+            }
+        };
+        v.accept(function);
+
+        if (!blockSrcrefs.isEmpty()) {
+            return RDataFactory.createList(blockSrcrefs.toArray());
+        }
+        return null;
+    }
+
     @TruffleBoundary
     public static Object createLloc(SourceSection src) {
         if (src == null) {
@@ -117,7 +208,8 @@ public class RSrcref {
             env = RDataFactory.createNewEnv("src");
             env.setClassAttr(RDataFactory.createStringVector(new String[]{"srcfilecopy", RRuntime.R_SRCFILE}, true));
             try {
-                Path path = Paths.get(RSource.getPathInternal(source));
+                String pathStr = RSource.getPathInternal(source);
+                Path path = Paths.get(pathStr != null ? pathStr : "");
                 env.put(SrcrefFields.filename.name(), path.toString());
                 env.put(SrcrefFields.fixedNewlines.name(), RRuntime.LOGICAL_TRUE);
                 String[] lines = new String[source.getLineCount()];
-- 
GitLab