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,