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 6c2f34cee14ff03ff4c37888c6de0e286e6b0296..eaa5cfbb487b3a1e1c357e329afb3189b833253d 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
@@ -454,7 +454,7 @@ final class REngine implements Engine, Engine.Timings {
             for (int i = 0; i < newArgs.length; i++) {
                 Object arg = newArgs[i];
                 if (arg instanceof RPromise) {
-                    newArgs[i] = PromiseHelperNode.evaluateSlowPath(null, (RPromise) arg);
+                    newArgs[i] = PromiseHelperNode.evaluateSlowPath((RPromise) arg);
                 }
             }
         }
@@ -681,6 +681,6 @@ final class REngine implements Engine, Engine.Timings {
     }
 
     private static Object evaluatePromise(Object value) {
-        return value instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) value) : value;
+        return value instanceof RPromise ? PromiseHelperNode.evaluateSlowPath((RPromise) value) : value;
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java
index 3d2a62fd7f04978fd81d4a64892f495e9a2200ce..b5e7c8ae1eb3dec0c024c9185b453d5e7ee94507 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleCompleter.java
@@ -78,7 +78,7 @@ public class JLineConsoleCompleter implements Completer {
             REnvironment utils = REnvironment.getRegisteredNamespace("utils");
             Object o = utils.get(".completeToken");
             if (o instanceof RPromise) {
-                o = PromiseHelperNode.evaluateSlowPath(null, (RPromise) o);
+                o = PromiseHelperNode.evaluateSlowPath((RPromise) o);
             }
             RFunction completeToken;
             if (o instanceof RFunction) {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
index 5ec3a485350a46e226516c39801a6203aeac81d3..00ac91621ba2c891c36d0fd298552d9fbf5a8823 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
@@ -423,7 +423,7 @@ public class MethodsListDispatch {
                 Object res = FrameSlotChangeMonitor.getValue(slot, currentFrame);
                 if (res != null) {
                     if (res instanceof RPromise) {
-                        res = PromiseHelperNode.evaluateSlowPath(null, (RPromise) res);
+                        res = PromiseHelperNode.evaluateSlowPath((RPromise) res);
                     }
                 }
                 return res;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
index 2e8a04837d0705b28bb39734801bcd3b40776db6..51bf9095613afaf8efa757bfabfa5ba2fc87b64b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
@@ -66,14 +66,14 @@ public abstract class Exists extends RBuiltinNode.Arg4 {
         if (!inherits) {
             Object obj = env.get(name);
             if (modeType != RType.Any && obj instanceof RPromise) {
-                obj = PromiseHelperNode.evaluateSlowPath(null, (RPromise) obj);
+                obj = PromiseHelperNode.evaluateSlowPath((RPromise) obj);
             }
             return RRuntime.asLogical(obj != null && RRuntime.checkType(obj, modeType));
         }
         for (REnvironment e = env; e != REnvironment.emptyEnv(); e = e.getParent()) {
             Object obj = e.get(name);
             if (modeType != RType.Any && obj instanceof RPromise) {
-                obj = PromiseHelperNode.evaluateSlowPath(null, (RPromise) obj);
+                obj = PromiseHelperNode.evaluateSlowPath((RPromise) obj);
             }
             if (obj != null && RRuntime.checkType(obj, modeType)) {
                 return RRuntime.LOGICAL_TRUE;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
index 2b33757ec46aa25f0170a3c31f779b4e120d4707..f4d636e57a42f4340d51a196310c18a5b377efdf 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
@@ -43,7 +43,6 @@ import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.PosixFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -315,9 +314,9 @@ public class FileFunctions {
                         PosixFileAttributes pfa = Files.readAttributes(path, PosixFileAttributes.class);
                         size = pfa.size();
                         isdir = RRuntime.asLogical(pfa.isDirectory());
-                        mtime = getTimeInSecs(pfa.lastModifiedTime());
-                        ctime = getTimeInSecs(pfa.creationTime());
-                        atime = getTimeInSecs(pfa.lastAccessTime());
+                        mtime = Utils.getTimeInSecs(pfa.lastModifiedTime());
+                        ctime = Utils.getTimeInSecs(pfa.creationTime());
+                        atime = Utils.getTimeInSecs(pfa.lastAccessTime());
                         uname = pfa.owner().getName();
                         grname = pfa.group().getName();
                         mode = Utils.intFilePermissions(pfa.permissions());
@@ -347,14 +346,6 @@ public class FileFunctions {
             return RDataFactory.createList(data, NAMES_VECTOR);
         }
 
-        private static int getTimeInSecs(Object fileTime) {
-            if (fileTime == null) {
-                return RRuntime.INT_NA;
-            } else {
-                return (int) ((FileTime) fileTime).toMillis() / 1000;
-            }
-        }
-
         private static Object createColumnData(Column column, int vecLength) {
             // @formatter:off
             switch(column) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java
index 8301704079762b789c5b79c3d1395fd6e5680ea8..a0e3caa5659487e3fd021c13191e4756a508ce73 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 
 @RBuiltin(name = "oldClass", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
@@ -56,6 +57,11 @@ public abstract class GetOldClass extends RBuiltinNode.Arg1 {
         }
     }
 
+    @Specialization
+    protected Object getOldClass(@SuppressWarnings("unused") RSymbol arg) {
+        return RNull.instance;
+    }
+
     @Specialization
     protected Object getOldClass(@SuppressWarnings("unused") RFunction arg) {
         return RNull.instance;
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 55e01a2343ef37ec8ace29e634f44409123869d7..d39308b1ce11a881d5752fedf1fc500ba68ae365 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
@@ -96,9 +96,9 @@ public abstract class Parse extends RBuiltinNode.Arg6 {
     @Child private CastStringNode castStringNode;
     @Child private CastToVectorNode castVectorNode;
 
-    @Child private SetFixedAttributeNode setSrcRefAttrNode = SetFixedAttributeNode.create("srcref");
-    @Child private SetFixedAttributeNode setWholeSrcRefAttrNode = SetFixedAttributeNode.create("wholeSrcref");
-    @Child private SetFixedAttributeNode setSrcFileAttrNode = SetFixedAttributeNode.create("srcfile");
+    @Child private SetFixedAttributeNode setSrcRefAttrNode = SetFixedAttributeNode.create(RRuntime.R_SRCREF);
+    @Child private SetFixedAttributeNode setWholeSrcRefAttrNode = SetFixedAttributeNode.create(RRuntime.R_WHOLE_SRCREF);
+    @Child private SetFixedAttributeNode setSrcFileAttrNode = SetFixedAttributeNode.create(RRuntime.R_SRCFILE);
 
     static {
         Casts casts = new Casts(Parse.class);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
index c58e054d13d7f8a640da45c7c0c134c4243714f8..a2f76704d12304fa76d7e7e413bd859021cf90b7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.GetNonSharedNode;
+import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -48,7 +49,8 @@ public abstract class UpdateNames extends RBuiltinNode.Arg2 {
     @Child private CastStringNode castStringNode;
 
     static {
-        Casts.noCasts(UpdateNames.class);
+        Casts casts = new Casts(UpdateNames.class);
+        casts.arg("x").mustNotBeNull(RError.Message.SET_ATTRIBUTES_ON_NULL);
     }
 
     private Object castString(Object o) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSourceInfo.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSourceInfo.java
index ed3430a7b325f4ad1d1f6abd39ecc8fb61e57102..5f9863bb15b9b99732b13ce28e22876aaab8e349 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSourceInfo.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSourceInfo.java
@@ -22,15 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 import static com.oracle.truffle.r.runtime.RVisibility.ON;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.builtins.RBehavior;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RNull;
 
 /**
@@ -40,25 +45,37 @@ import com.oracle.truffle.r.runtime.data.RNull;
 public abstract class FastRSourceInfo extends RBuiltinNode.Arg1 {
 
     static {
-        Casts.noCasts(FastRSourceInfo.class);
+        Casts casts = new Casts(FastRSourceInfo.class);
+        casts.arg("fun").defaultError(RError.Message.GENERIC, "Only functions are allowed.").mustBe(instanceOf(RFunction.class).or(instanceOf(RLanguage.class)));
     }
 
     @Specialization
-    public Object srcInfo(@SuppressWarnings("unused") RNull fun) {
-        return RNull.instance;
+    public Object srcInfo(RFunction fun) {
+        return srcInfo(fun.getRootNode());
     }
 
     @Specialization
-    public Object srcInfo(RFunction fun) {
-        SourceSection ss = fun.getRootNode().getSourceSection();
+    public Object srcInfo(RLanguage fun) {
+        return srcInfo(fun.getRep());
+    }
+
+    private Object srcInfo(Node fun) {
+        SourceSection ss = fun.getSourceSection();
         if (ss != null) {
             String path = ss.getSource().getPath();
             if (path != null) {
                 return path + "#" + ss.getStartLine();
+            } else if (ss.getSource().getURI() != null) {
+                return ss.getSource().getURI() + "#" + ss.getStartLine();
             } else {
                 return ss.getSource().getName();
             }
         }
         return RNull.instance;
     }
+
+    @Fallback
+    public Object srcInfo(@SuppressWarnings("unused") Object o) {
+        return RNull.instance;
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java
index 75a82d67668729717c7458700c24757ba51b08f8..71a45e61427f6db63500611e90e59991cd859f64 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java
@@ -73,7 +73,7 @@ public abstract class FastRTreeStats extends RBuiltinNode.Arg1 {
             String binding = bindings.getDataAt(i);
             Object value = env.get(binding);
             if (value instanceof RPromise) {
-                value = PromiseHelperNode.evaluateSlowPath(null, (RPromise) value);
+                value = PromiseHelperNode.evaluateSlowPath((RPromise) value);
 
             }
             if (value instanceof RFunction) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/tools/R/tools_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/tools/R/tools_overrides.R
new file mode 100644
index 0000000000000000000000000000000000000000..0899b8303b49a63a02bb3669bf319aa373637eab
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/tools/R/tools_overrides.R
@@ -0,0 +1,165 @@
+#
+# 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) 1995-2015, The R Core Team
+# Copyright (c) 2003, The R Foundation
+# Copyright (c) 2017, Oracle and/or its affiliates
+#
+# All rights reserved.
+#
+
+
+
+# The modified function ".install_package_code_files" copies the source 
+# file of the package to be installed into the installation directory 
+# (see comments starting with "FastR extension:").
+# The code is then parsed from these files such that the parsed
+# elements are associated with the source files in the installation 
+# directory.
+
+eval(expression({
+.install_package_code_files <-
+function(dir, outDir)
+{
+    if(!dir.exists(dir))
+        stop(gettextf("directory '%s' does not exist", dir),
+             domain = NA)
+    dir <- file_path_as_absolute(dir)
+
+    ## Attempt to set the LC_COLLATE locale to 'C' to turn off locale
+    ## specific sorting.
+    curLocale <- Sys.getlocale("LC_COLLATE")
+    on.exit(Sys.setlocale("LC_COLLATE", curLocale), add = TRUE)
+    ## (Guaranteed to work as per the Sys.setlocale() docs.)
+    lccollate <- "C"
+    if(Sys.setlocale("LC_COLLATE", lccollate) != lccollate) {
+        ## <NOTE>
+        ## I don't think we can give an error here.
+        ## It may be the case that Sys.setlocale() fails because the "OS
+        ## reports request cannot be honored" (src/main/platform.c), in
+        ## which case we should still proceed ...
+        warning("cannot turn off locale-specific sorting via LC_COLLATE")
+        ## </NOTE>
+    }
+
+    ## We definitely need a valid DESCRIPTION file.
+    db <- .read_description(file.path(dir, "DESCRIPTION"))
+
+    codeDir <- file.path(dir, "R")
+    if(!dir.exists(codeDir)) return(invisible())
+
+    codeFiles <- list_files_with_type(codeDir, "code", full.names = FALSE)
+
+    collationField <-
+        c(paste("Collate", .OStype(), sep = "."), "Collate")
+    if(any(i <- collationField %in% names(db))) {
+        collationField <- collationField[i][1L]
+        codeFilesInCspec <- .read_collate_field(db[collationField])
+        ## Duplicated entries in the collation spec?
+        badFiles <-
+            unique(codeFilesInCspec[duplicated(codeFilesInCspec)])
+        if(length(badFiles)) {
+            out <- gettextf("\nduplicated files in '%s' field:",
+                            collationField)
+            out <- paste(out,
+                         paste(" ", badFiles, collapse = "\n"),
+                         sep = "\n")
+            stop(out, domain = NA)
+        }
+        ## See which files are listed in the collation spec but don't
+        ## exist.
+        badFiles <- setdiff(codeFilesInCspec, codeFiles)
+        if(length(badFiles)) {
+            out <- gettextf("\nfiles in '%s' field missing from '%s':",
+                            collationField,
+                            codeDir)
+            out <- paste(out,
+                         paste(" ", badFiles, collapse = "\n"),
+                         sep = "\n")
+            stop(out, domain = NA)
+        }
+        ## See which files exist but are missing from the collation
+        ## spec.  Note that we do not want the collation spec to use
+        ## only a subset of the available code files.
+        badFiles <- setdiff(codeFiles, codeFilesInCspec)
+        if(length(badFiles)) {
+            out <- gettextf("\nfiles in '%s' missing from '%s' field:",
+                            codeDir,
+                            collationField)
+            out <- paste(out,
+                         paste(" ", badFiles, collapse = "\n"),
+                         sep = "\n")
+            stop(out, domain = NA)
+        }
+        ## Everything's groovy ...
+        codeFiles <- codeFilesInCspec
+    }
+
+    codeFiles <- file.path(codeDir, codeFiles)
+
+    if(!dir.exists(outDir) && !dir.create(outDir))
+        stop(gettextf("cannot open directory '%s'", outDir),
+             domain = NA)
+    outCodeDir <- file.path(outDir, "R")
+    if(!dir.exists(outCodeDir) && !dir.create(outCodeDir))
+        stop(gettextf("cannot open directory '%s'", outCodeDir),
+             domain = NA)
+    outFile <- file.path(outCodeDir, db["Package"])
+    if(!file.create(outFile))
+        stop(gettextf("unable to create '%s'", outFile), domain = NA)
+    writeLines(paste0(".packageName <- \"", db["Package"], "\""),
+               outFile)
+    enc <- as.vector(db["Encoding"])
+    need_enc <- !is.na(enc) # Encoding was specified
+    ## assume that if locale is 'C' we can used 8-bit encodings unchanged.
+    if(need_enc && !(Sys.getlocale("LC_CTYPE") %in% c("C", "POSIX"))) {
+        con <- file(outFile, "a")
+        on.exit(close(con))  # Windows does not like files left open
+        for(f in codeFiles) {
+            tmp <- iconv(readLines(f, warn = FALSE), from = enc, to = "")
+            if(length(bad <- which(is.na(tmp)))) {
+                warning(sprintf(ngettext(length(bad),
+                                         "unable to re-encode %s line %s",
+                                         "unable to re-encode %s lines %s"),
+                                sQuote(basename(f)),
+                                paste(bad, collapse = ", ")),
+                        domain = NA, call. = FALSE)
+                tmp <- iconv(readLines(f, warn = FALSE), from = enc, to = "",
+                             sub = "byte")
+            }
+
+			# FastR extension: also copy original source file
+            singleOutFile <- file.path(outCodeDir, basename(f))
+            writeLines(tmp, file(singleOutFile))
+
+            writeLines(paste0("#line 1 \"", singleOutFile, "\""), con)
+            writeLines(tmp, con)
+        }
+	close(con); on.exit()
+    } else {
+        ## <NOTE>
+        ## It may be safer to do
+        ##   writeLines(sapply(codeFiles, readLines), outFile)
+        ## instead, but this would be much slower ...
+        ## use fast version of file.append that ensures LF between files
+
+		# FastR extension: also copy original source file
+        singleOutFiles <- file.path(outCodeDir, basename(codeFiles))
+        file.copy(codeFiles, singleOutFiles)
+
+        if(!all(.file_append_ensuring_LFs(outFile, singleOutFiles)))
+            stop("unable to write code files")
+        ## </NOTE>
+    }
+    ## A syntax check here, so that we do not install a broken package.
+    ## FIXME:  this is only needed if we don't lazy load, as the lazy loader
+    ## would detect the error.
+    op <- options(showErrorCalls=FALSE)
+    on.exit(options(op))
+    parse(outFile)
+    invisible()
+}
+
+}), asNamespace("tools"))
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index d784b3d0a1157993e6d33dc7483cabf2e9c2fd66..dc171181ab5335a264ed0dc034392008002875f6 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -755,7 +755,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
                         throw RError.error(RError.SHOW_CALLER, RError.Message.ARGUMENT_MISSING, identifier);
                     }
                     if (value instanceof RPromise) {
-                        return PromiseHelperNode.evaluateSlowPath(null, (RPromise) value);
+                        return PromiseHelperNode.evaluateSlowPath((RPromise) value);
                     }
                     return value;
                 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
index 7e8564b4ecffa5f5b5fb7e88e6c2c72bb18df1fd..0ce9148bd595ef13b9a3a6a175f26ab6590cea02 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
@@ -249,6 +249,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         return extractedVector;
     }
 
+    @TruffleBoundary
     private Object doEnvironment(REnvironment env, Object[] positions) {
         if (mode.isSubset()) {
             throw error(RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName());
@@ -258,7 +259,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         if (positionString != null) {
             Object obj = env.get(positionString);
             if (promiseInEnvironment.profile(obj instanceof RPromise)) {
-                obj = PromiseHelperNode.evaluateSlowPath(null, (RPromise) obj);
+                obj = PromiseHelperNode.evaluateSlowPath((RPromise) obj);
             }
             return obj == null ? RNull.instance : obj;
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
index b635984a2cb511bea0b9716cacf2003f3c1470ea..ade76a40145d1f6dfe9dd841ea13f7a49e28f986 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java
@@ -223,6 +223,11 @@ public class PromiseHelperNode extends RBaseNode {
         return generateValueDefault(frame, promise);
     }
 
+    @TruffleBoundary
+    public static Object evaluateSlowPath(RPromise promise) {
+        return evaluateSlowPath(null, promise);
+    }
+
     public static Object evaluateSlowPath(VirtualFrame frame, RPromise promise) {
         CompilerAsserts.neverPartOfCompilation();
         if (promise.isEvaluated()) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
index fb72a30e6767f4d7cfe16c7646084ecc216de57a..a954484576061db50e18ce78152450a2b5c67c36 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
@@ -529,6 +529,10 @@ public abstract class PromiseNode extends RNode {
                     size++;
                     evaluatedArgs[i] = promiseCheckHelper.checkEvaluate(frame, argValue);
                 }
+                if (evaluatedArgs[i] == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    throw RInternalError.shouldNotReachHere("evaluated argument must not be null");
+                }
             }
             if (containsVarargProfile.profile(containsVarargs)) {
                 return size;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
index b520673ff942295815c0e2bb8b3a5c26a78e8abe..7447a2e1f7dddae8c2cc3d7d3a3ae2e60ff97515 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
@@ -109,7 +109,7 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
         Result result;
         Object methodsTable = getTable.get();
         if (methodsTable instanceof RPromise) {
-            methodsTable = PromiseHelperNode.evaluateSlowPath(null, (RPromise) methodsTable);
+            methodsTable = PromiseHelperNode.evaluateSlowPath((RPromise) methodsTable);
         }
         MaterializedFrame methodsTableFrame = methodsTable == null ? null : ((REnvironment) methodsTable).getFrame();
 
@@ -173,7 +173,7 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
 
     private static RFunction checkPromise(Object value) {
         if (value instanceof RPromise) {
-            return (RFunction) PromiseHelperNode.evaluateSlowPath(null, (RPromise) value);
+            return (RFunction) PromiseHelperNode.evaluateSlowPath((RPromise) value);
         } else {
             return (RFunction) value;
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
index f8b51386ac4fa75a5a54bd348c0889c2dae88ed1..c5fe1e156efc33ac413d46e50f47f837a7cc0f4c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/CollectGenericArgumentsNode.java
@@ -120,7 +120,7 @@ public abstract class CollectGenericArgumentsNode extends RBaseNode {
             } else {
                 Object value = FrameSlotChangeMonitor.getValue(slot, frame);
                 if (value instanceof RPromise) {
-                    value = PromiseHelperNode.evaluateSlowPath(null, (RPromise) value);
+                    value = PromiseHelperNode.evaluateSlowPath((RPromise) value);
                 }
                 result[i] = classHierarchyNodeSlowPath.executeString(value);
             }
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
index 76e8351cb9a82020724fb76869eac71177c84b17..1af7872f3177f094f90ad57e44d6637f0b6e7f76 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java
@@ -89,6 +89,7 @@ public class ParserGeneration {
         "support for hex float literals",
         "support for hex float literals without decimal point: 0x0p0",
         "different warning for hex and dec integer literals",
-        "raise ZERO_LENGTH_VARIABLE errors in parser"
+        "raise ZERO_LENGTH_VARIABLE errors in parser",
+        "support for file delimiter"
     };
 }
diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
index c1e9f36e56e3b8eb98586c9157f5268bda0ab90b..83669ea5c758a81555e67978ea83f85f04459938 100644
--- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
+++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g
@@ -28,6 +28,7 @@ package com.oracle.truffle.r.parser;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.io.IOException;
 
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -39,6 +40,7 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
@@ -60,7 +62,9 @@ import com.oracle.truffle.r.runtime.RError;
 
 @members {
     private Source source;
+    private Source initialSource;
     private RCodeBuilder<T> builder;
+    private int fileStartOffset = 0;
     
     /**
      * Always use this constructor to initialize the R specific fields.
@@ -69,6 +73,7 @@ import com.oracle.truffle.r.runtime.RError;
         super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCode()))));
         assert source != null && builder != null;
         this.source = source;
+        this.initialSource = source;
         this.builder = builder;
     }
     
@@ -101,8 +106,13 @@ import com.oracle.truffle.r.runtime.RError;
      */
     private SourceSection src(Token t) {
         CommonToken token = (CommonToken) t;
-        int startIndex = token.getStartIndex();
-        return source.createSection(startIndex, token.getStopIndex() - startIndex + 1);
+        try {
+        	return source.createSection(token.getStartIndex() - fileStartOffset, token.getStopIndex() - token.getStartIndex() + 1);
+        } catch(IllegalArgumentException e) {
+        	// fall back and use the initial source (the file being parsed) 
+        	resetSource();
+        	return source.createSection(token.getStartIndex(), token.getStopIndex() - token.getStartIndex() + 1);
+        }
     }
     
     /**
@@ -114,8 +124,43 @@ import com.oracle.truffle.r.runtime.RError;
         int startIndex = cstart.getStartIndex();
         int stopIndex = cstop.getStopIndex();
         int length = stopIndex - startIndex + (cstop.getType() == Token.EOF ? 0 : 1);
-        return source.createSection(startIndex, length);
+        try {
+        	return source.createSection(startIndex - fileStartOffset, length);
+        } catch(IllegalArgumentException e) {
+        	// fall back and use the initial source (the file being parsed) 
+        	resetSource();
+        	return source.createSection(startIndex, length);
+        }
     }
+    
+    /**
+     * Checks if the token is a comment token indicating that the following part of the source file was
+     * pasted from another file.
+     * The format of this file delimiter is '#line 1 "filename"'
+     */
+    private void checkFileDelim(CommonToken commentToken) {
+			String commentLine = commentToken.getText();
+	    	if(commentLine.startsWith("#line 1")) {
+	    		int q0 = commentLine.indexOf("\"");
+	    		int q1 = commentLine.indexOf("\"", q0+1);
+	    		if(q0 != -1 && q1 != -1) {
+	    			String path = commentLine.substring(q0+1, q1);
+	    			try {
+                        source = RSource.fromFileName(path, false);
+                        fileStartOffset = commentToken.getStopIndex() + 1;
+                    } catch (IOException e) {
+                    }
+	    		} else {
+	    			// fall back and use the initial source (the file being parsed)
+	    			resetSource();
+	    		}
+	    	}
+	    }
+	
+	private void resetSource() {
+		source = initialSource;
+		fileStartOffset = 0;
+	}
 
 	// without this override, the parser will not throw exceptions if it can recover    
     @Override
@@ -205,9 +250,12 @@ statement returns [T v]
     : e=expr_or_assign n_one { $v = $e.v; }
     ;
 
-n_ : (NEWLINE | COMMENT)*;
-n_one  : (NEWLINE | COMMENT)+ | EOF | SEMICOLON n_;
-n_multi  : (NEWLINE | COMMENT | SEMICOLON)+ | EOF;
+n_ : (NEWLINE | COMMENT { checkFileDelim((CommonToken)last()); } 
+     )*;
+n_one  : (NEWLINE | COMMENT { checkFileDelim((CommonToken)last()); }
+         )+ | EOF | SEMICOLON n_;
+n_multi  : (NEWLINE | COMMENT { checkFileDelim((CommonToken)last()); }
+            | SEMICOLON)+ | EOF;
 
 expr_wo_assign returns [T v]
     : w=while_expr                                      { $v = $w.v; }
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 85b7cd674b25d9306ebe7c1bc94c08fd13183f2d..410a9d3b4f89474c1fc5e5f36acfd473257aa4f8 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
@@ -19,6 +19,7 @@ import java.io.OutputStream;
 import java.io.PrintStream;
 import java.lang.ref.WeakReference;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.NoSuchFileException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Deque;
@@ -38,7 +39,11 @@ import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.frame.FrameSlotKind;
 import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrumentation.StandardTags;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
@@ -580,6 +585,7 @@ public class RSerialize {
                             RFunction func = PairlistDeserializer.processFunction(carItem, cdrItem, tagItem, currentFunctionName, packageName);
                             if (attrItem != RNull.instance) {
                                 setAttributes(func, attrItem);
+                                handleFunctionSrcrefAttr(func);
                             }
                             result = func;
                             break;
@@ -894,6 +900,7 @@ public class RSerialize {
                     pl = (RPairList) cdr;
                 }
             }
+
             return result;
         }
 
@@ -1427,259 +1434,254 @@ public class RSerialize {
                     outRefIndex(refIndex);
                 } else if (type == SEXPTYPE.SYMSXP) {
                     writeSymbol((RSymbol) obj);
-                } else if (type == SEXPTYPE.ENVSXP) {
-                    REnvironment env = (REnvironment) obj;
-                    addReadRef(obj);
-                    String name = null;
-                    if ((name = env.isPackageEnv()) != null) {
-                        RError.warning(RError.SHOW_CALLER2, RError.Message.PACKAGE_AVAILABLE, name);
-                        stream.writeInt(SEXPTYPE.PACKAGESXP.code);
-                        stream.writeString(name);
-                    } else if (env.isNamespaceEnv()) {
-                        stream.writeInt(SEXPTYPE.NAMESPACESXP.code);
-                        RStringVector nameSpaceEnvSpec = env.getNamespaceSpec();
-                        outStringVec(nameSpaceEnvSpec, false);
-                    } else {
-                        stream.writeInt(SEXPTYPE.ENVSXP.code);
-                        stream.writeInt(env.isLocked() ? 1 : 0);
-                        writeItem(env.getParent());
-                        /*
-                         * TODO To be truly compatible with GnuR we should remember whether an
-                         * environment was created with new.env(hash=T) and output it in that form
-                         * with the associated size. For internal FastR use it does not matter, so
-                         * we use the "frame" form, which is a pairlist. tag is binding name, car is
-                         * binding value
-                         */
-                        String[] bindings = env.ls(true, null, false).getDataWithoutCopying();
-                        for (String binding : bindings) {
-                            Object value = getValueIgnoreActiveBinding(env.getFrame(), binding);
-                            writePairListEntry(binding, value);
-                        }
-                        terminatePairList();
-                        writeItem(RNull.instance); // hashtab
-                        DynamicObject attributes = env.getAttributes();
-                        if (attributes != null) {
-                            writeAttributes(attributes);
-                        } else {
-                            writeItem(RNull.instance);
-                        }
-                    }
                 } else {
-                    // flags
-                    DynamicObject attributes = null;
-                    if (obj instanceof RAttributable) {
-                        RAttributable rattr = (RAttributable) obj;
-                        attributes = rattr.getAttributes();
-                        if (attributes != null && attributes.isEmpty()) {
-                            attributes = null;
-                        }
-                    }
-                    boolean hasTag = gnuRType == SEXPTYPE.CLOSXP || gnuRType == SEXPTYPE.DOTSXP || (gnuRType == SEXPTYPE.PROMSXP && !((RPromise) obj).isEvaluated()) ||
-                                    (type == SEXPTYPE.LISTSXP && !((RPairList) obj).isNullTag());
-                    int gpbits = getGPBits(obj);
-                    int flags = Flags.packFlags(gnuRType, gpbits, isObject(obj), attributes != null, hasTag);
-                    stream.writeInt(flags);
-                    switch (type) {
-                        case STRSXP: {
-                            if (obj instanceof String) {
-                                // length 1 vector
-                                stream.writeInt(1);
-                                writeCHARSXP((String) obj);
+                    if (type == SEXPTYPE.ENVSXP) {
+                        REnvironment env = (REnvironment) obj;
+                        addReadRef(obj);
+                        String name = null;
+                        if ((name = env.isPackageEnv()) != null) {
+                            RError.warning(RError.SHOW_CALLER2, RError.Message.PACKAGE_AVAILABLE, name);
+                            stream.writeInt(SEXPTYPE.PACKAGESXP.code);
+                            stream.writeString(name);
+                        } else if (env.isNamespaceEnv()) {
+                            stream.writeInt(SEXPTYPE.NAMESPACESXP.code);
+                            RStringVector nameSpaceEnvSpec = env.getNamespaceSpec();
+                            outStringVec(nameSpaceEnvSpec, false);
+                        } else {
+                            stream.writeInt(SEXPTYPE.ENVSXP.code);
+                            stream.writeInt(env.isLocked() ? 1 : 0);
+                            writeItem(env.getParent());
+                            /*
+                             * TODO To be truly compatible with GnuR we should remember whether an
+                             * environment was created with new.env(hash=T) and output it in that
+                             * form with the associated size. For internal FastR use it does not
+                             * matter, so we use the "frame" form, which is a pairlist. tag is
+                             * binding name, car is binding value
+                             */
+                            String[] bindings = env.ls(true, null, false).getDataWithoutCopying();
+                            for (String binding : bindings) {
+                                Object value = getValueIgnoreActiveBinding(env.getFrame(), binding);
+                                writePairListEntry(binding, value);
+                            }
+                            terminatePairList();
+                            writeItem(RNull.instance); // hashtab
+                            OutAttributes attributes = new OutAttributes(env, type, gnuRType);
+                            if (attributes.hasAttributes()) {
+                                writeAttributes(attributes);
                             } else {
-                                outStringVec((RAbstractStringVector) obj, true);
+                                writeItem(RNull.instance);
                             }
-                            break;
                         }
+                    } else {
+                        // flags
+                        OutAttributes attributes = new OutAttributes(obj, type, gnuRType);
+                        boolean hasTag = gnuRType == SEXPTYPE.CLOSXP || gnuRType == SEXPTYPE.DOTSXP || (gnuRType == SEXPTYPE.PROMSXP && !((RPromise) obj).isEvaluated()) ||
+                                        (type == SEXPTYPE.LISTSXP && !((RPairList) obj).isNullTag());
+                        int gpbits = getGPBits(obj);
+                        int flags = Flags.packFlags(gnuRType, gpbits, isObject(obj), attributes.hasAttributes(), hasTag);
+                        stream.writeInt(flags);
+                        switch (type) {
+                            case STRSXP: {
+                                if (obj instanceof String) {
+                                    // length 1 vector
+                                    stream.writeInt(1);
+                                    writeCHARSXP((String) obj);
+                                } else {
+                                    outStringVec((RAbstractStringVector) obj, true);
+                                }
+                                break;
+                            }
 
-                        case INTSXP: {
-                            if (obj instanceof Integer) {
-                                stream.writeInt(1);
-                                stream.writeInt((int) obj);
-                            } else {
-                                RAbstractIntVector vec = (RAbstractIntVector) obj;
-                                stream.writeInt(vec.getLength());
-                                for (int i = 0; i < vec.getLength(); i++) {
-                                    stream.writeInt(vec.getDataAt(i));
+                            case INTSXP: {
+                                if (obj instanceof Integer) {
+                                    stream.writeInt(1);
+                                    stream.writeInt((int) obj);
+                                } else {
+                                    RAbstractIntVector vec = (RAbstractIntVector) obj;
+                                    stream.writeInt(vec.getLength());
+                                    for (int i = 0; i < vec.getLength(); i++) {
+                                        stream.writeInt(vec.getDataAt(i));
+                                    }
                                 }
+                                break;
                             }
-                            break;
-                        }
 
-                        case REALSXP: {
-                            if (obj instanceof Double) {
-                                stream.writeInt(1);
-                                stream.writeDouble((double) obj);
-                            } else {
-                                RAbstractDoubleVector vec = (RAbstractDoubleVector) obj;
-                                stream.writeInt(vec.getLength());
-                                for (int i = 0; i < vec.getLength(); i++) {
-                                    stream.writeDouble(vec.getDataAt(i));
+                            case REALSXP: {
+                                if (obj instanceof Double) {
+                                    stream.writeInt(1);
+                                    stream.writeDouble((double) obj);
+                                } else {
+                                    RAbstractDoubleVector vec = (RAbstractDoubleVector) obj;
+                                    stream.writeInt(vec.getLength());
+                                    for (int i = 0; i < vec.getLength(); i++) {
+                                        stream.writeDouble(vec.getDataAt(i));
+                                    }
                                 }
+                                break;
                             }
-                            break;
-                        }
 
-                        case LGLSXP: {
-                            // Output as ints
-                            if (obj instanceof Byte) {
-                                stream.writeInt(1);
-                                stream.writeInt(RRuntime.logical2int((byte) obj));
-                            } else {
-                                RAbstractLogicalVector vec = (RAbstractLogicalVector) obj;
+                            case LGLSXP: {
+                                // Output as ints
+                                if (obj instanceof Byte) {
+                                    stream.writeInt(1);
+                                    stream.writeInt(RRuntime.logical2int((byte) obj));
+                                } else {
+                                    RAbstractLogicalVector vec = (RAbstractLogicalVector) obj;
+                                    stream.writeInt(vec.getLength());
+                                    for (int i = 0; i < vec.getLength(); i++) {
+                                        stream.writeInt(RRuntime.logical2int(vec.getDataAt(i)));
+                                    }
+                                }
+                                break;
+                            }
+
+                            case CPLXSXP: {
+                                RAbstractComplexVector vec = (RAbstractComplexVector) obj;
                                 stream.writeInt(vec.getLength());
                                 for (int i = 0; i < vec.getLength(); i++) {
-                                    stream.writeInt(RRuntime.logical2int(vec.getDataAt(i)));
+                                    RComplex val = vec.getDataAt(i);
+                                    if (RRuntime.isNA(val)) {
+                                        stream.writeDouble(RRuntime.DOUBLE_NA);
+                                        stream.writeDouble(RRuntime.DOUBLE_NA);
+                                    } else {
+                                        stream.writeDouble(val.getRealPart());
+                                        stream.writeDouble(val.getImaginaryPart());
+                                    }
                                 }
+                                break;
                             }
-                            break;
-                        }
 
-                        case CPLXSXP: {
-                            RAbstractComplexVector vec = (RAbstractComplexVector) obj;
-                            stream.writeInt(vec.getLength());
-                            for (int i = 0; i < vec.getLength(); i++) {
-                                RComplex val = vec.getDataAt(i);
-                                if (RRuntime.isNA(val)) {
-                                    stream.writeDouble(RRuntime.DOUBLE_NA);
-                                    stream.writeDouble(RRuntime.DOUBLE_NA);
+                            case EXPRSXP:
+                            case VECSXP: {
+                                RAbstractVector list;
+                                if (type == SEXPTYPE.EXPRSXP) {
+                                    list = (RExpression) obj;
                                 } else {
-                                    stream.writeDouble(val.getRealPart());
-                                    stream.writeDouble(val.getImaginaryPart());
+                                    list = (RList) obj;
+                                }
+                                stream.writeInt(list.getLength());
+                                for (int i = 0; i < list.getLength(); i++) {
+                                    Object listObj = list.getDataAtAsObject(i);
+                                    writeItem(listObj);
                                 }
+                                break;
                             }
-                            break;
-                        }
 
-                        case EXPRSXP:
-                        case VECSXP: {
-                            RAbstractVector list;
-                            if (type == SEXPTYPE.EXPRSXP) {
-                                list = (RExpression) obj;
-                            } else {
-                                list = (RList) obj;
-                            }
-                            stream.writeInt(list.getLength());
-                            for (int i = 0; i < list.getLength(); i++) {
-                                Object listObj = list.getDataAtAsObject(i);
-                                writeItem(listObj);
+                            case RAWSXP: {
+                                RRawVector raw = (RRawVector) obj;
+                                byte[] data = raw.getDataWithoutCopying();
+                                stream.writeInt(data.length);
+                                stream.writeRaw(data);
+                                break;
                             }
-                            break;
-                        }
 
-                        case RAWSXP: {
-                            RRawVector raw = (RRawVector) obj;
-                            byte[] data = raw.getDataWithoutCopying();
-                            stream.writeInt(data.length);
-                            stream.writeRaw(data);
-                            break;
-                        }
-
-                        case EXTPTRSXP: {
-                            addReadRef(obj);
-                            RExternalPtr xptr = (RExternalPtr) obj;
-                            writeItem(xptr.getProt());
-                            writeItem(xptr.getTag());
-                            break;
-                        }
-
-                        case S4SXP: {
-                            break;
-                        }
-
-                        /*
-                         * The objects that GnuR represents as a pairlist. To avoid stack overflow,
-                         * these utilize manual tail recursion on the cdr of the pairlist.
-                         */
-
-                        case FUNSXP:
-                        case PROMSXP:
-                        case LANGSXP:
-                        case LISTSXP:
-                        case DOTSXP: {
-                            if (type == SEXPTYPE.FUNSXP && gnuRType == SEXPTYPE.BUILTINSXP) {
-                                // special case
-                                RFunction fun = (RFunction) obj;
-                                String name = fun.getRBuiltin().getName();
-                                stream.writeString(name);
+                            case EXTPTRSXP: {
+                                addReadRef(obj);
+                                RExternalPtr xptr = (RExternalPtr) obj;
+                                writeItem(xptr.getProt());
+                                writeItem(xptr.getTag());
                                 break;
                             }
-                            tailCall = true;
-                            // attributes written first to avoid recursion on cdr
-                            if (attributes != null) {
-                                writeAttributes(attributes);
-                                attributes = null;
+
+                            case S4SXP: {
+                                break;
                             }
 
-                            switch (type) {
-                                case FUNSXP: {
+                            /*
+                             * The objects that GnuR represents as a pairlist. To avoid stack
+                             * overflow, these utilize manual tail recursion on the cdr of the
+                             * pairlist.closePairList
+                             */
+
+                            case FUNSXP:
+                            case PROMSXP:
+                            case LANGSXP:
+                            case LISTSXP:
+                            case DOTSXP: {
+                                if (type == SEXPTYPE.FUNSXP && gnuRType == SEXPTYPE.BUILTINSXP) {
+                                    // special case
                                     RFunction fun = (RFunction) obj;
-                                    RPairList pl = (RPairList) serializeLanguageObject(state, fun);
-                                    assert pl != null;
-                                    state.convertUnboundValues(pl);
-                                    if (FastROptions.debugMatches("printWclosure")) {
-                                        Debug.printClosure(pl);
-                                    }
-                                    writeItem(pl.getTag());
-                                    writeItem(pl.car());
-                                    obj = pl.cdr();
+                                    String name = fun.getRBuiltin().getName();
+                                    stream.writeString(name);
                                     break;
                                 }
+                                tailCall = true;
 
-                                case PROMSXP: {
-                                    RPairList pl = (RPairList) serializeLanguageObject(state, obj);
-                                    assert pl != null;
-                                    state.convertUnboundValues(pl);
-                                    if (pl.getTag() != RNull.instance) {
-                                        writeItem(pl.getTag());
-                                    }
-                                    writeItem(pl.car());
-                                    obj = pl.cdr();
-                                    break;
+                                // attributes written first to avoid recursion on cdr
+                                writeAttributes(attributes);
+                                if (attributes != null) {
+                                    attributes = null;
                                 }
 
-                                case LISTSXP: {
-                                    RPairList pl = (RPairList) obj;
-                                    if (!pl.isNullTag()) {
+                                switch (type) {
+                                    case FUNSXP: {
+                                        RFunction fun = (RFunction) obj;
+                                        RPairList pl = (RPairList) serializeLanguageObject(state, fun);
+                                        assert pl != null;
+                                        state.convertUnboundValues(pl);
+                                        if (FastROptions.debugMatches("printWclosure")) {
+                                            Debug.printClosure(pl);
+                                        }
                                         writeItem(pl.getTag());
+                                        writeItem(pl.car());
+                                        obj = pl.cdr();
+                                        break;
                                     }
-                                    writeItem(pl.car());
-                                    obj = pl.cdr();
-                                    break;
-                                }
 
-                                case LANGSXP: {
-                                    RPairList pl = (RPairList) serializeLanguageObject(state, obj);
-                                    state.convertUnboundValues(pl);
-                                    writeItem(pl.car());
-                                    obj = pl.cdr();
-                                    break;
-                                }
+                                    case PROMSXP: {
+                                        RPairList pl = (RPairList) serializeLanguageObject(state, obj);
+                                        assert pl != null;
+                                        state.convertUnboundValues(pl);
+                                        if (pl.getTag() != RNull.instance) {
+                                            writeItem(pl.getTag());
+                                        }
+                                        writeItem(pl.car());
+                                        obj = pl.cdr();
+                                        break;
+                                    }
 
-                                case DOTSXP: {
-                                    // This in GnuR is a pairlist
-                                    RArgsValuesAndNames rvn = (RArgsValuesAndNames) obj;
-                                    Object list = RNull.instance;
-                                    for (int i = rvn.getLength() - 1; i >= 0; i--) {
-                                        String name = rvn.getSignature().getName(i);
-                                        list = RDataFactory.createPairList(rvn.getArgument(i), list, name == null ? RNull.instance : RDataFactory.createSymbolInterned(name));
+                                    case LISTSXP: {
+                                        RPairList pl = (RPairList) obj;
+                                        if (!pl.isNullTag()) {
+                                            writeItem(pl.getTag());
+                                        }
+                                        writeItem(pl.car());
+                                        obj = pl.cdr();
+                                        break;
                                     }
-                                    RPairList pl = (RPairList) list;
-                                    if (!pl.isNullTag()) {
-                                        writeItem(pl.getTag());
+
+                                    case LANGSXP: {
+                                        RPairList pl = (RPairList) serializeLanguageObject(state, obj);
+                                        state.convertUnboundValues(pl);
+                                        writeItem(pl.car());
+                                        obj = pl.cdr();
+                                        break;
+                                    }
+
+                                    case DOTSXP: {
+                                        // This in GnuR is a pairlist
+                                        RArgsValuesAndNames rvn = (RArgsValuesAndNames) obj;
+                                        Object list = RNull.instance;
+                                        for (int i = rvn.getLength() - 1; i >= 0; i--) {
+                                            String name = rvn.getSignature().getName(i);
+                                            list = RDataFactory.createPairList(rvn.getArgument(i), list, name == null ? RNull.instance : RDataFactory.createSymbolInterned(name));
+                                        }
+                                        RPairList pl = (RPairList) list;
+                                        if (!pl.isNullTag()) {
+                                            writeItem(pl.getTag());
+                                        }
+                                        writeItem(pl.car());
+                                        obj = pl.cdr();
+                                        break;
                                     }
-                                    writeItem(pl.car());
-                                    obj = pl.cdr();
-                                    break;
                                 }
+                                break;
                             }
-                            break;
-                        }
 
-                        default:
-                            throw RInternalError.unimplemented(type.name());
-                    }
+                            default:
+                                throw RInternalError.unimplemented(type.name());
+                        }
 
-                    if (attributes != null) {
                         writeAttributes(attributes);
                     }
                 }
@@ -1755,17 +1757,34 @@ public class RSerialize {
             }
         }
 
-        private void writeAttributes(DynamicObject attributes) throws IOException {
-            // have to convert to GnuR pairlist
-            Iterator<RAttributesLayout.RAttribute> iter = RAttributesLayout.asIterable(attributes).iterator();
-            while (iter.hasNext()) {
-                RAttributesLayout.RAttribute attr = iter.next();
-                // name is the tag of the virtual pairlist
-                // value is the car
-                // next is the cdr
-                writePairListEntry(attr.getName(), attr.getValue());
+        private void writeAttributes(OutAttributes outAttrs) throws IOException {
+            if (outAttrs != null) {
+                SourceSection ss = outAttrs.getSourceReferenceAttributes();
+                if (ss != null) {
+                    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) {
+                    // have to convert to GnuR pairlist
+                    Iterator<RAttributesLayout.RAttribute> iter = RAttributesLayout.asIterable(attributes).iterator();
+                    while (iter.hasNext()) {
+                        RAttributesLayout.RAttribute attr = iter.next();
+                        // name is the tag of the virtual pairlist
+                        // value is the car
+                        // next is the cdr
+                        writePairListEntry(attr.getName(), attr.getValue());
+                    }
+                }
+                if (outAttrs.hasAttributes()) {
+                    terminatePairList();
+                }
             }
-            terminatePairList();
         }
 
         private void writePairListEntry(String name, Object value) throws IOException {
@@ -2072,6 +2091,50 @@ public class RSerialize {
         }
     }
 
+    /**
+     * An abstraction of implicit and explicit attributes to serialize.
+     */
+    private static class OutAttributes {
+        private DynamicObject explicitAttributes;
+        private SourceSection ss;
+
+        private OutAttributes(Object obj, SEXPTYPE type, SEXPTYPE gnuRType) {
+
+            if (obj instanceof RAttributable) {
+                explicitAttributes = ((RAttributable) obj).getAttributes();
+            }
+            initSourceSection(obj, type, gnuRType);
+        }
+
+        private void initSourceSection(Object obj, SEXPTYPE type, SEXPTYPE gnuRType) {
+            if (type == SEXPTYPE.FUNSXP && gnuRType != SEXPTYPE.BUILTINSXP) {
+                RFunction fun = (RFunction) obj;
+                RSyntaxFunction body = (RSyntaxFunction) fun.getRootNode();
+                setSourceSection(body);
+            }
+        }
+
+        private void setSourceSection(RSyntaxElement body) {
+            SourceSection lazySourceSection = body.getLazySourceSection();
+            if (RSource.getPathInternal(lazySourceSection.getSource()) != null) {
+                ss = lazySourceSection;
+            }
+        }
+
+        public boolean hasAttributes() {
+            return explicitAttributes != null && !explicitAttributes.isEmpty() || ss != null;
+        }
+
+        public DynamicObject getExplicitAttributes() {
+            return explicitAttributes;
+        }
+
+        public SourceSection getSourceReferenceAttributes() {
+            return ss;
+        }
+
+    }
+
     /**
      * For {@code lazyLoadDBinsertValue}.
      */
@@ -2195,7 +2258,7 @@ public class RSerialize {
                     String name = ((RSyntaxLookup) lhs).getIdentifier();
                     infixFieldAccess = "$".equals(name) || "@".equals(name);
                 }
-                serializeArguments(arguments, element.getSyntaxSignature(), infixFieldAccess);
+                serializeArguments(element, arguments, element.getSyntaxSignature(), infixFieldAccess);
             }
             return null;
         }
@@ -2208,7 +2271,7 @@ public class RSerialize {
             return false;
         }
 
-        private void serializeArguments(RSyntaxElement[] arguments, ArgumentsSignature signature, boolean infixFieldAccess) {
+        private void serializeArguments(RSyntaxCall callElement, RSyntaxElement[] arguments, ArgumentsSignature signature, boolean infixFieldAccess) {
             state.openPairList(SEXPTYPE.LISTSXP);
             if (arguments.length == 0) {
                 state.setNull();
@@ -2239,7 +2302,11 @@ public class RSerialize {
                 }
                 state.linkPairList(arguments.length);
             }
-            state.setCdr(state.closePairList());
+            Object pl = state.closePairList();
+            if (callElement instanceof Node && RContext.getRRuntimeASTAccess().isTaggedWith((Node) callElement, StandardTags.StatementTag.class)) {
+                attachSrcref(callElement, pl);
+            }
+            state.setCdr(pl);
         }
 
         @Override
@@ -2341,13 +2408,52 @@ public class RSerialize {
         REnvironment env = REnvironment.frameToEnvironment(f.getEnclosingFrame());
         state.openPairList().setTag(env);
         serializeFunctionDefinition(state, function);
-        return state.closePairList();
+        Object pl = state.closePairList();
+        return pl;
     }
 
     private static void serializeFunctionDefinition(State state, RSyntaxFunction function) {
         SerializeVisitor visitor = new SerializeVisitor(state);
         state.setCar(visitor.visitFunctionFormals(function));
-        state.setCdr(visitor.visitFunctionBody(function));
+        Object body = visitor.visitFunctionBody(function);
+
+        // convert and attach source section to srcref attribute
+        attachSrcref(function, body);
+
+        state.setCdr(body);
+    }
+
+    /**
+     * Converts the source section from the syntax element to a srcref attribute and attaches it to
+     * the serialization object.
+     *
+     * @param syntaxElement The syntax element providing the source section.
+     * @param serObj The object to attribute (most likely a pair list).
+     */
+    private static void attachSrcref(RSyntaxElement syntaxElement, Object serObj) {
+        SourceSection ss = getFileSourceSection(syntaxElement);
+        if (ss != null && serObj instanceof RAttributable) {
+            String pathInternal = RSource.getPathInternal(ss.getSource());
+            RAttributable attributable = (RAttributable) serObj;
+            attributable.setAttr(RRuntime.R_SRCFILE, RSrcref.createSrcfile(pathInternal));
+            RList createBlockSrcrefs = RSrcref.createBlockSrcrefs(syntaxElement);
+            if (createBlockSrcrefs != null) {
+                attributable.setAttr(RRuntime.R_SRCREF, createBlockSrcrefs);
+                attributable.setAttr(RRuntime.R_WHOLE_SRCREF, RSrcref.createLloc(ss));
+            } else {
+                Object createLloc = RSrcref.createLloc(ss);
+                attributable.setAttr(RRuntime.R_SRCREF, createLloc);
+                attributable.setAttr(RRuntime.R_WHOLE_SRCREF, RSrcref.createLloc(ss));
+            }
+        }
+    }
+
+    private static SourceSection getFileSourceSection(RSyntaxElement syntaxElement) {
+        SourceSection ss = syntaxElement.getLazySourceSection();
+        if (ss != null && RSource.getPathInternal(ss.getSource()) != null) {
+            return ss;
+        }
+        return null;
     }
 
     private static Object serializeLanguage(State state, RLanguage lang) {
@@ -2387,9 +2493,10 @@ public class RSerialize {
     private static final class PairlistDeserializer {
 
         public static RFunction processFunction(Object car, Object cdr, Object tag, String functionName, String packageName) {
-            // car == arguments, cdr == body, tag == environment
+            // car == arguments, cdr == body, tag == PairList(attributes, environment)
 
             REnvironment environment = (REnvironment) tag;
+
             MaterializedFrame enclosingFrame = environment.getFrame();
             RootCallTarget callTarget = RContext.getASTBuilder().rootFunction(RSyntaxNode.LAZY_DEPARSE, processArguments(car), processBody(cdr), functionName);
 
@@ -2460,7 +2567,11 @@ public class RSerialize {
                 RPairList function = (RPairList) cdr;
                 return processFunctionExpression(function.car(), function.cdr(), function.getTag());
             }
-            return RContext.getASTBuilder().call(RSyntaxNode.LAZY_DEPARSE, process(car, true), processArguments(cdr));
+            RSyntaxNode call = RContext.getASTBuilder().call(RSyntaxNode.LAZY_DEPARSE, process(car, true), processArguments(cdr));
+            if (cdr instanceof RAttributable) {
+                handleSrcrefAttr((RAttributable) cdr, call);
+            }
+            return call;
         }
 
         private static RSyntaxNode processFunctionExpression(Object car, Object cdr, @SuppressWarnings("unused") Object tag) {
@@ -2494,20 +2605,83 @@ public class RSerialize {
         private static RSyntaxNode processBody(Object cdr) {
             if (cdr instanceof RPairList) {
                 RPairList pl = (RPairList) cdr;
+                RSyntaxNode body;
                 switch (pl.getType()) {
                     case BCODESXP:
                         RAbstractListVector list = (RAbstractListVector) pl.cdr();
-                        return process(list.getDataAtAsObject(0), false);
+                        body = process(list.getDataAtAsObject(0), false);
+                        break;
                     case LISTSXP:
                         assert pl.cdr() == RNull.instance || (pl.cadr() == RNull.instance && pl.cddr() == RNull.instance);
-                        return process(pl.car(), false);
+                        body = process(pl.car(), false);
+                        break;
                     case LANGSXP:
-                        return processCall(pl.car(), pl.cdr(), pl.getTag());
+                        body = processCall(pl.car(), pl.cdr(), pl.getTag());
+                        break;
                     default:
                         throw RInternalError.shouldNotReachHere("unexpected SXP type in body: " + pl.getType());
                 }
+                handleSrcrefAttr(pl, body);
+                return body;
             }
             return process(cdr, false);
         }
     }
+
+    private static void handleFunctionSrcrefAttr(RFunction func) {
+        handleSrcrefAttr(func, (RSyntaxElement) func.getRootNode());
+    }
+
+    /**
+     * @param func Element carrying the {@value RRuntime#R_SRCREF} attribute.
+     * @param elem The syntax element to create the source section for.
+     */
+    private static void handleSrcrefAttr(RAttributable func, RSyntaxElement elem) {
+        if (elem instanceof RSyntaxCall) {
+            handleSrcrefAttr(func, (RSyntaxCall) elem);
+        } else {
+            Object srcref = func.getAttr(RRuntime.R_SRCREF);
+            if (srcref instanceof RAbstractIntVector) {
+                SourceSection ss = RSrcref.createSourceSection((RAbstractIntVector) srcref, null);
+                elem.setSourceSection(ss);
+            }
+        }
+    }
+
+    /**
+     * @param func Element carrying the {@value RRuntime#R_SRCREF} attribute.
+     * @param elem The syntax element to create the source section for.
+     */
+    private static void handleSrcrefAttr(RAttributable func, RSyntaxCall elem) {
+        Object srcref = func.getAttr(RRuntime.R_SRCREF);
+        if (srcref instanceof RAbstractIntVector) {
+            SourceSection ss = RSrcref.createSourceSection((RAbstractIntVector) srcref, null);
+            elem.setSourceSection(ss);
+        } else if (srcref instanceof RList) {
+            try {
+                Object srcfile = func.getAttr(RRuntime.R_SRCFILE);
+                assert srcfile instanceof REnvironment;
+                Source source = RSource.fromSrcfile((REnvironment) srcfile);
+
+                RList l = (RList) srcref;
+                RSyntaxElement[] syntaxArguments = elem.getSyntaxArguments();
+                assert syntaxArguments.length == l.getLength() - 1;
+
+                for (int i = 0; i < l.getLength(); i++) {
+                    Object dataAt = l.getDataAt(i);
+                    assert dataAt instanceof RAbstractIntVector;
+                    SourceSection ss = RSrcref.createSourceSection((RAbstractIntVector) dataAt, source);
+                    if (i == 0) {
+                        elem.setSourceSection(ss);
+                    } else {
+                        syntaxArguments[i - 1].setSourceSection(ss);
+                    }
+                }
+            } catch (NoSuchFileException e) {
+                RError.warning(RError.SHOW_CALLER, RError.Message.GENERIC, "Missing source file: " + e.getMessage());
+            } catch (IOException e) {
+                RError.warning(RError.SHOW_CALLER, RError.Message.GENERIC, "Cannot access source file: " + e.getMessage());
+            }
+        }
+    }
 }
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 16f3e223d16b0c343dba84208bd6e57da4c54d0e..e4bdde9ff45ac1d7c3b801028d1081d55d32fcf8 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
@@ -27,9 +27,13 @@ import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.runtime.RSrcref.SrcrefFields;
+import com.oracle.truffle.r.runtime.env.REnvironment;
 
 /**
  * A facade for the creation of Truffle {@link Source} objects, which is complicated in R due the
@@ -187,6 +191,18 @@ public class RSource {
         return Source.newBuilder(url).name(name).mimeType(RRuntime.R_APP_MIME).build();
     }
 
+    /**
+     * Create an (external) source from an R srcfile ({@link RSrcref#createSrcfile(String)}).
+     */
+    public static Source fromSrcfile(REnvironment env) throws IOException {
+        Path filename = Paths.get((String) RRuntime.r2Java(env.get(SrcrefFields.filename.name())));
+        if (filename.isAbsolute()) {
+            return fromFileName(filename.toString(), false);
+        }
+        Path wd = Paths.get((String) RRuntime.r2Java(env.get(SrcrefFields.wd.name())));
+        return fromFileName(wd.resolve(filename).toString(), false);
+    }
+
     /**
      * Create an unknown source with the given name.
      */
@@ -221,6 +237,18 @@ public class RSource {
         return uri.getPath();
     }
 
+    /**
+     * Like {@link #getPath(Source)} but ignoring if {@code source} is "internal".
+     */
+    public static String getPathInternal(Source source) {
+        if (source == null) {
+            return null;
+        }
+        URI uri = source.getURI();
+        assert uri != null;
+        return uri.getPath();
+    }
+
     /**
      * Always returns a non-null string even for internal sources.
      */
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 d2a27b63774482b121dc835c85c905c982377e17..63c82fd5e0eaa8dda66b6952f055c0f5c19d4de9 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
@@ -14,8 +14,12 @@ package com.oracle.truffle.r.runtime;
 import java.io.IOException;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
+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;
@@ -23,12 +27,21 @@ 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;
 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},
@@ -50,7 +63,9 @@ public class RSrcref {
         timestamp,
         filename,
         isFile,
-        wd;
+        wd,
+        fixedNewlines,
+        lines;
     }
 
     private static final RStringVector SRCREF_ATTR = RDataFactory.createStringVectorFromScalar(RRuntime.R_SRCREF);
@@ -66,22 +81,28 @@ public class RSrcref {
     @TruffleBoundary
     private static REnvironment createSrcfile(Path path) {
         // A srcref is an environment
-        double mtime;
+        REnvironment env = RContext.getInstance().srcfileEnvironments.get(path);
+        if (env == null) {
+            env = RDataFactory.createNewEnv("");
+            env.safePut(SrcrefFields.Enc.name(), "unknown");
+            env.safePut(SrcrefFields.encoding.name(), "native.enc");
+            env.safePut(SrcrefFields.timestamp.name(), getTimestamp(path));
+            env.safePut(SrcrefFields.filename.name(), path.toString());
+            env.safePut(SrcrefFields.isFile.name(), RRuntime.LOGICAL_TRUE);
+            env.safePut(SrcrefFields.wd.name(), BaseRFFI.GetwdRootNode.create().getCallTarget().call());
+            env.setClassAttr(SRCFILE_ATTR);
+            RContext.getInstance().srcfileEnvironments.put(path, env);
+        }
+        return env;
+    }
+
+    private static int getTimestamp(Path path) {
         try {
             PosixFileAttributes pfa = Files.readAttributes(path, PosixFileAttributes.class);
-            mtime = pfa.lastModifiedTime().toMillis();
+            return Utils.getTimeInSecs(pfa.lastModifiedTime());
         } catch (IOException ex) {
-            mtime = RRuntime.DOUBLE_NA;
+            return RRuntime.INT_NA;
         }
-        REnvironment env = RDataFactory.createNewEnv("");
-        env.safePut(SrcrefFields.Enc.name(), "unknown");
-        env.safePut(SrcrefFields.encoding.name(), "native.enc");
-        env.safePut(SrcrefFields.timestamp.name(), mtime);
-        env.safePut(SrcrefFields.filename.name(), path.toString());
-        env.safePut(SrcrefFields.isFile.name(), RRuntime.LOGICAL_TRUE);
-        env.safePut(SrcrefFields.wd.name(), BaseRFFI.GetwdRootNode.create().getCallTarget().call());
-        env.setClassAttr(SRCFILE_ATTR);
-        return env;
     }
 
     /**
@@ -93,6 +114,35 @@ 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(RSyntaxElement function) {
+
+        BlockSrcrefsVisitor v = new BlockSrcrefsVisitor();
+        v.accept(function);
+        List<Object> blockSrcrefs = v.blockSrcrefs;
+
+        if (!blockSrcrefs.isEmpty()) {
+            return RDataFactory.createList(blockSrcrefs.toArray());
+        }
+        return null;
+    }
+
     @TruffleBoundary
     public static Object createLloc(SourceSection src) {
         if (src == null) {
@@ -107,13 +157,19 @@ public class RSrcref {
             env = RDataFactory.createNewEnv("src");
             env.setClassAttr(RDataFactory.createStringVector(new String[]{"srcfilecopy", RRuntime.R_SRCFILE}, true));
             try {
-                env.put("filename", source.getPath() == null ? "" : source.getPath());
-                env.put("fixedNewlines", RRuntime.LOGICAL_TRUE);
+                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()];
                 for (int i = 0; i < lines.length; i++) {
                     lines[i] = source.getCode(i + 1);
                 }
-                env.put("lines", RDataFactory.createStringVector(lines, true));
+                env.put(SrcrefFields.lines.name(), RDataFactory.createStringVector(lines, true));
+                env.safePut(SrcrefFields.Enc.name(), "unknown");
+                env.safePut(SrcrefFields.isFile.name(), RRuntime.asLogical(Files.isRegularFile(path)));
+                env.safePut(SrcrefFields.timestamp.name(), getTimestamp(path));
+                env.safePut(SrcrefFields.wd.name(), BaseRFFI.GetwdRootNode.create().getCallTarget().call());
             } catch (PutException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
@@ -149,4 +205,98 @@ public class RSrcref {
         lloc.setAttr(RRuntime.R_SRCFILE, srcfile);
         return lloc;
     }
+
+    public static SourceSection createSourceSection(RAbstractIntVector srcrefVec, Source sharedSource) {
+
+        try {
+            Source source;
+            if (sharedSource != null) {
+                source = sharedSource;
+            } else {
+                Object srcfile = srcrefVec.getAttr(RRuntime.R_SRCFILE);
+                assert srcfile instanceof REnvironment;
+                source = RSource.fromSrcfile((REnvironment) srcfile);
+            }
+            int startLine = srcrefVec.getDataAt(0);
+            int startColumn = srcrefVec.getDataAt(1);
+            int startIdx = getLineStartOffset(source, startLine) + startColumn;
+            int length = getLineStartOffset(source, srcrefVec.getDataAt(2)) + srcrefVec.getDataAt(3) - startIdx + 1;
+            return source.createSection(startLine, startColumn, length);
+        } catch (NoSuchFileException e) {
+            RError.warning(RError.SHOW_CALLER, RError.Message.GENERIC, "Missing source file: " + e.getMessage());
+        } catch (IOException e) {
+            RError.warning(RError.SHOW_CALLER, RError.Message.GENERIC, "Cannot access source file: " + e.getMessage());
+        } catch (IllegalArgumentException e) {
+            RError.warning(RError.SHOW_CALLER, RError.Message.GENERIC, "Invalid source reference: " + e.getMessage());
+        }
+        return RSourceSectionNode.LAZY_DEPARSE;
+    }
+
+    private static int getLineStartOffset(Source source, int lineNum) {
+        try {
+            return source.getLineStartOffset(lineNum);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException(String.format("line %d does not exist in source %s", lineNum, RSource.getPathInternal(source)), e);
+        }
+    }
+
+    private static final class BlockSrcrefsVisitor extends RSyntaxVisitor<Void> {
+        private List<Object> blockSrcrefs = new LinkedList<>();
+        private int depth = 0;
+
+        @Override
+        public Void visit(RSyntaxCall element) {
+
+            if (depth == 0 && !isBlockStatement(element)) {
+                return null;
+            }
+
+            addSrcref(blockSrcrefs, element);
+
+            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 static void addSrcref(List<Object> blockSrcrefs, RSyntaxElement element) {
+            SourceSection lazySourceSection = element.getLazySourceSection();
+            if (lazySourceSection != null) {
+                blockSrcrefs.add(createLloc(lazySourceSection));
+            }
+        }
+
+        private static 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) {
+            addSrcref(blockSrcrefs, element);
+            return null;
+        }
+
+        @Override
+        public Void visit(RSyntaxLookup element) {
+            addSrcref(blockSrcrefs, element);
+            return null;
+        }
+
+        @Override
+        public Void visit(RSyntaxFunction element) {
+            accept(element.getSyntaxBody());
+            return null;
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index a511ed968943b46d2c5da96149c8453114209c04..5d3c31feba257afdbfd70dbc0da57b7c367a7f19 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -30,6 +30,7 @@ import java.io.InputStreamReader;
 import java.nio.file.FileSystem;
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.PosixFilePermission;
 import java.util.Arrays;
 import java.util.Set;
@@ -718,4 +719,12 @@ public final class Utils {
         }
         return startingTempDir;
     }
+
+    public static int getTimeInSecs(FileTime fileTime) {
+        if (fileTime == null) {
+            return RRuntime.INT_NA;
+        } else {
+            return (int) (fileTime.toMillis() / 1000);
+        }
+    }
 }
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 e49f250dae488b0679e9becaf528227aef5af405..affd6bef10ef5dc829e42e72a9bca1b7211eb572 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
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.runtime.context;
 
 import java.io.Closeable;
 import java.lang.ref.WeakReference;
+import java.nio.file.Path;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
@@ -454,6 +455,7 @@ public final class RContext extends com.oracle.truffle.api.ExecutionContext impl
 
     public final WeakHashMap<String, WeakReference<String>> stringMap = new WeakHashMap<>();
     public final WeakHashMap<Source, REnvironment> sourceRefEnvironments = new WeakHashMap<>();
+    public final WeakHashMap<Path, REnvironment> srcfileEnvironments = new WeakHashMap<>();
 
     private ContextState[] contextStates() {
         return new ContextState[]{stateREnvVars, stateRProfile, stateTempPath, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI,