From 09c77535abfdca42c494cb94debec6f6a08c52c5 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Fri, 30 Dec 2016 14:57:07 +0100
Subject: [PATCH] create proper srcref attributes for function literals

---
 .../r/engine/RRuntimeASTAccessImpl.java       |  7 ++-
 .../engine/interop/ffi/TruffleCallHelper.java |  3 +-
 .../truffle/r/runtime/RErrorHandling.java     |  2 -
 .../com/oracle/truffle/r/runtime/RSource.java | 47 +++++++++++++++++++
 .../truffle/r/runtime/context/RContext.java   |  1 +
 .../truffle/r/test/ExpectedTestOutput.test    | 28 +++++++++++
 .../r/test/functions/TestFunctions.java       |  5 ++
 7 files changed, 85 insertions(+), 8 deletions(-)

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 161c25219a..a3211b25f0 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -68,11 +68,11 @@ import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntimeASTAccess;
+import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.Engine;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RComplex;
@@ -211,9 +211,8 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
                     result = ((RSyntaxFunction) s).getSyntaxBody();
                     break;
                 case 3:
-                    // TODO: handle srcref properly - for now, clearly mark an erroneous access to
-                    // this piece of data
-                    return new RArgsValuesAndNames(new String[]{"DUMMY UNIMPLEMENTED SRCREF"}, ArgumentsSignature.get("dummy"));
+                    // srcref
+                    return RSource.createSrcRef(s.getLazySourceSection());
                 default:
                     throw RInternalError.shouldNotReachHere();
             }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/TruffleCallHelper.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/TruffleCallHelper.java
index 4347e0e1d9..2daf1027e7 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/TruffleCallHelper.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/TruffleCallHelper.java
@@ -26,14 +26,13 @@ import java.nio.charset.StandardCharsets;
 
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.r.engine.interop.NativeRawArray;
 import com.oracle.truffle.r.engine.interop.NativeCharArray;
 import com.oracle.truffle.r.engine.interop.NativeDoubleArray;
 import com.oracle.truffle.r.engine.interop.NativeIntegerArray;
 import com.oracle.truffle.r.engine.interop.NativeLogicalArray;
+import com.oracle.truffle.r.engine.interop.NativeRawArray;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDouble;
 import com.oracle.truffle.r.runtime.data.RInteger;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
index 9fcf43914e..db7e5ccde4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
@@ -584,8 +584,6 @@ public class RErrorHandling {
         /*
          * Warnings generally do not prevent results being printed. However, this call into R will
          * destroy any visibility setting made by the calling builtin prior to this call.
-         *
-         * TODO: it's not clear whether this is still the case with the optimized visibility scheme
          */
         ContextStateImpl errorHandlingState = getRErrorHandlingState();
         RFunction f = errorHandlingState.getDotSignalSimpleWarning();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java
index 75b14bc3ea..db3bbbbf03 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java
@@ -30,6 +30,13 @@ import java.net.URL;
 
 import com.oracle.truffle.api.source.Source;
 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.RNull;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
  * A facade for the creation of Truffle {@link Source} objects, which is complicated in R due the
@@ -202,4 +209,44 @@ public class RSource {
         }
     }
 
+    public static Object createSrcRef(SourceSection src) {
+        if (src == null) {
+            return RNull.instance;
+        }
+        if (src == RSyntaxNode.INTERNAL || src == RSyntaxNode.LAZY_DEPARSE || src == RSyntaxNode.SOURCE_UNAVAILABLE) {
+            return RNull.instance;
+        }
+        Source source = src.getSource();
+        REnvironment env = RContext.getInstance().sourceRefEnvironments.get(source);
+        if (env == null) {
+            env = RDataFactory.createNewEnv("src");
+            env.setClassAttr(RDataFactory.createStringVector(new String[]{"srcfilecopy", "srcfile"}, true));
+            try {
+                env.put("filename", source.getPath() == null ? "" : source.getPath());
+                env.put("fixedNewlines", RRuntime.LOGICAL_TRUE);
+                String[] lines = new String[source.getLineCount()];
+                for (int i = 0; i < lines.length; i++) {
+                    lines[i] = source.getCode(i + 1);
+                }
+                env.put("lines", RDataFactory.createStringVector(lines, true));
+            } catch (PutException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+            RContext.getInstance().sourceRefEnvironments.put(source, env);
+        }
+        /*
+         * TODO: it's unclear what the exact format is, experimentally it is (first line, first
+         * column, last line, last column, first column, last column, first line, last line). the
+         * second pair of columns is likely bytes instead of chars, and the second pair of lines
+         * parsed as opposed to "real" lines (may be modified by #line).
+         */
+        int startLine = src.getStartLine();
+        int startColumn = src.getStartColumn();
+        int endLine = src.getEndLine();
+        int endColumn = src.getEndColumn();
+        RIntVector ref = RDataFactory.createIntVector(new int[]{startLine, startColumn, endLine, endColumn, startColumn, endColumn, startLine, endLine}, true);
+        ref.setAttr("srcfile", env);
+        ref.setClassAttr(RDataFactory.createStringVector("srcref"));
+        return ref;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index b1564503a0..a140e87e36 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -418,6 +418,7 @@ public final class RContext extends ExecutionContext implements TruffleObject {
     private ContextState stateRFFI;
 
     public final WeakHashMap<String, WeakReference<String>> stringMap = new WeakHashMap<>();
+    public final WeakHashMap<Source, REnvironment> sourceRefEnvironments = new WeakHashMap<>();
 
     private ContextState[] contextStates() {
         return new ContextState[]{stateREnvVars, stateRProfile, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI, stateRSerialize,
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index dc3c97220c..69fdd24326 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -58762,6 +58762,34 @@ NULL
 ##com.oracle.truffle.r.test.functions.TestFunctions.testReturn#
 #{ f<-function() { return(invisible(2)) } ; f() }
 
+##com.oracle.truffle.r.test.functions.TestFunctions.testSrcref#
+#1<<<NEWLINE>>>f <- quote(function(x, y) <<<NEWLINE>>> a + <<<NEWLINE>>> foooo); as.list(f); as.list(f)[[4]]; unclass(as.list(f)[[4]]); class(as.list(f)[[4]])
+[1] 1
+[[1]]
+`function`
+
+[[2]]
+[[2]]$x
+
+
+[[2]]$y
+
+
+
+[[3]]
+a + foooo
+
+[[4]]
+function(x, y)
+ a +
+ foooo
+
+function(x, y)
+ a +
+ foooo
+[1]  1 12  3  6 12  6  1  3
+[1] "srcref"
+
 ##com.oracle.truffle.r.test.functions.TestFunctions.testStateTransitions#
 #{ f<-function(x) { l<-length(x); x[1]<-1 }; y<-c(42,7); f(y); y }
 [1] 42  7
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java
index 78259d758d..d3cd786a1a 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/TestFunctions.java
@@ -400,4 +400,9 @@ public class TestFunctions extends TestBase {
     public void testConversions() {
         assertEval("{ x<-quote(list(...)); l<-list(); l[[2]]<-x; names(l)<-c(\"...\"); f<-as.function(l); f(7, 42) }");
     }
+
+    @Test
+    public void testSrcref() {
+        assertEval("1\nf <- quote(function(x, y) \n a + \n foooo); as.list(f); as.list(f)[[4]]; unclass(as.list(f)[[4]]); class(as.list(f)[[4]])");
+    }
 }
-- 
GitLab