Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
841 lines (750 sloc)
22.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # SessionEnvironment.R | |
| # | |
| # Copyright (C) 2021 by RStudio, PBC | |
| # | |
| # Unless you have received this program directly from RStudio pursuant | |
| # to the terms of a commercial license agreement with RStudio, then | |
| # this program is licensed to you under the terms of version 3 of the | |
| # GNU Affero General Public License. This program is distributed WITHOUT | |
| # ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT, | |
| # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the | |
| # AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details. | |
| # | |
| # | |
| .rs.addJsonRpcHandler("is_function_masked", function(functionName, | |
| packageName) | |
| { | |
| result <- tryCatch( | |
| .rs.isFunctionMasked(functionName, packageName), | |
| error = function(e) FALSE | |
| ) | |
| .rs.scalar(result) | |
| }) | |
| .rs.addFunction("isFunctionMasked", function(functionName, | |
| packageName) | |
| { | |
| globalValue <- get( | |
| x = functionName, | |
| envir = globalenv(), | |
| mode = "function" | |
| ) | |
| packageValue <- get( | |
| x = functionName, | |
| envir = asNamespace(packageName), | |
| mode = "function" | |
| ) | |
| !identical(globalValue, packageValue) | |
| }) | |
| .rs.addFunction("valueFromStr", function(val) | |
| { | |
| .rs.withTimeLimit(1, fail = "<truncated>", { | |
| capture.output(try(str(val), silent = TRUE)) | |
| }) | |
| }) | |
| .rs.addFunction("valueAsString", function(val) | |
| { | |
| tryCatch( | |
| .rs.valueAsStringImpl(val), | |
| error = function(e) "NO_VALUE" | |
| ) | |
| }) | |
| .rs.addFunction("valueAsStringImpl", function(val) | |
| { | |
| if (is.null(val)) | |
| { | |
| "NULL" | |
| } | |
| else if (is.character(val) && !is.object(val)) | |
| { | |
| # for plain character variables, we 'mock' the behavior of str() | |
| # while avoiding the potential for re-encoding (as this could mangle | |
| # UTF-8 characters on Windows) | |
| n <- length(val) | |
| if (n == 0) | |
| { | |
| "character (empty)" | |
| } | |
| else if (n == 1) | |
| { | |
| encodeString(val, quote = "\"", na.encode = FALSE) | |
| } | |
| else | |
| { | |
| encoded <- encodeString( | |
| head(val, n = 128L), | |
| quote = "\"", | |
| na.encode = FALSE | |
| ) | |
| fmt <- "chr [1:%i] %s" | |
| txt <- sprintf(fmt, n, paste(encoded, collapse = " ")) | |
| .rs.truncate(txt) | |
| } | |
| } | |
| else if (is.raw(val)) | |
| { | |
| if (length(val) == 0) | |
| { | |
| "raw (empty)" | |
| } | |
| else | |
| { | |
| .rs.valueFromStr(val) | |
| } | |
| } | |
| else if (is.atomic(val) && is.null(attributes(val))) | |
| { | |
| n <- length(val) | |
| if (n == 0) | |
| { | |
| paste(.rs.getSingleClass(val), " (empty)") | |
| } | |
| else if (n == 1) | |
| { | |
| .rs.truncate(.rs.deparse(val), 1024L) | |
| } | |
| else | |
| { | |
| .rs.valueFromStr(val) | |
| } | |
| } | |
| else if (inherits(val, "python.builtin.object")) | |
| { | |
| .rs.valueFromStr(val) | |
| } | |
| else if (.rs.isFunction(val)) | |
| { | |
| .rs.getSignature(val) | |
| } | |
| else if (inherits(val, c("Date", "POSIXct", "POSIXlt"))) | |
| { | |
| if (length(val) == 1) | |
| { | |
| format(val, usetz = TRUE) | |
| } | |
| else | |
| { | |
| .rs.valueFromStr(val) | |
| } | |
| } | |
| else | |
| { | |
| "NO_VALUE" | |
| } | |
| }) | |
| .rs.addFunction("valueContents", function(val) | |
| { | |
| tryCatch( | |
| .rs.valueContentsImpl(val), | |
| error = function(e) "NO_VALUE" | |
| ) | |
| }) | |
| .rs.addFunction("valueContentsImpl", function(val) | |
| { | |
| if (inherits(val, "ore.frame")) | |
| { | |
| # for Oracle R frames, show the query if it exists | |
| query <- attr(val, "dataQry", exact = TRUE) | |
| # no query, show empty | |
| if (is.null(query)) | |
| return("NO_VALUE") | |
| # query, display it | |
| attributes(query) <- NULL | |
| paste(" Query:", query) | |
| } | |
| else if (inherits(val, "pandas.core.frame.DataFrame")) | |
| { | |
| output <- reticulate::py_to_r(val$to_string(max_rows = 150L)) | |
| strsplit(output, "\n", fixed = TRUE)[[1]] | |
| } | |
| else | |
| { | |
| output <- .rs.valueFromStr(val) | |
| n <- length(output) | |
| # only return the first 150 lines of detail (generally columns)--any more | |
| # won't be very presentable in the environment pane. the first line | |
| # generally contains descriptive text, so don't return that. | |
| if (n > 150) | |
| { | |
| fmt <- " [... %i lines omitted]" | |
| tail <- sprintf(fmt, n - 150) | |
| output <- c(output[2:150], tail) | |
| } | |
| else if (n > 1) | |
| { | |
| output <- output[-1] | |
| } | |
| output | |
| } | |
| }) | |
| .rs.addFunction("isFunction", function(val) | |
| { | |
| is.function(val) || identical(.rs.getSingleClass(val), "C++Function") | |
| }) | |
| # used to create description for promises | |
| .rs.addFunction("promiseDescription", function(obj) | |
| { | |
| # NOTE: substitute() returns the expression associated with a promise, | |
| # without forcing it to be evaluated | |
| expr <- substitute(obj) | |
| # if this appears to be a call to 'lazyLoadDBfetch()', that implies | |
| # this is lazy-loaded data (typically associated with an R package). | |
| # handle those up front | |
| isLazyLoad <- | |
| is.call(expr) && | |
| is.name(expr[[1L]]) && | |
| identical(expr[[1L]], as.name("lazyLoadDBfetch")) | |
| if (isLazyLoad) | |
| return("<Promise>") | |
| # for some calls, e.g. those formed manually or those evaluated via | |
| # do.call(), the 'expression' here might just be an already-evaluated | |
| # R object. in such a case, we want to avoid deparsing the object as | |
| # it could be expensive (especially for large objects). | |
| if (is.call(expr)) | |
| .rs.deparse(expr) | |
| else | |
| .rs.valueDescription(expr) | |
| }) | |
| # used to create descriptions for language objects and symbols | |
| .rs.addFunction("languageDescription", function(env, objectName) | |
| { | |
| desc <- "Missing object" | |
| tryCatch( | |
| { | |
| desc <- capture.output(print(get(objectName, env))) | |
| }, | |
| error = function(e) | |
| { | |
| # silently ignore; if the object can't be retrieved from the | |
| # environment, treat it as missing | |
| }, | |
| finally = | |
| { | |
| return(desc) | |
| }) | |
| }) | |
| .rs.addFunction("sourceFileFromRef", function(srcref) | |
| { | |
| if (!is.null(srcref)) | |
| { | |
| fileattr <- attr(srcref, "srcfile") | |
| enc2utf8(fileattr$filename) | |
| } | |
| else | |
| "" | |
| }) | |
| # Given a function and some content inside that function, returns a vector | |
| # in the standard R source reference format that represents the location of | |
| # the content in the deparsed representation of the function. | |
| .rs.addFunction("simulateSourceRefs", function(var) | |
| { | |
| # Read arguments from attached attributes to the input (these can't be passed | |
| # naked from RStudio) | |
| fun <- attr(var, "_rs_callfun") | |
| call <- attr(var, "_rs_callobj") | |
| calltext <- attr(var, "_rs_calltext") | |
| linepref <- attr(var, "_rs_lastline") | |
| # To proceed, we need the function to look in, and either the raw call | |
| # object (which we will deparse later) or the text to look for. | |
| if (is.null(fun) || | |
| (is.null(call) && is.null(calltext)) ) | |
| return(c(0L, 0L, 0L, 0L, 0L, 0L)) | |
| lines <- .rs.deparseFunction(fun, FALSE, FALSE) | |
| # Remember the indentation level on each line (added by deparse), and remove | |
| # it along with any other leading or trailing whitespace. | |
| indents <- nchar(sub("\\S.*", "", lines)) | |
| slines <- sub("\\s+$", "", sub("^\\s+", "", lines)) | |
| # Compute the character position of the start of each line, and collapse the | |
| # lines to a character vector of length 1. | |
| nchars <- 0 | |
| offsets <- integer(length(slines)) | |
| for (i in 1:length(slines)) { | |
| nchars <- nchars + nchar(slines[i]) + 1 | |
| offsets[i] <- nchars | |
| } | |
| singleline <- paste(slines, collapse=" ") | |
| if (is.null(calltext)) | |
| { | |
| # No call text specified; deparse into a list of lines | |
| calltext <- deparse(call) | |
| } | |
| else | |
| { | |
| # Call text specified as a single character vector; split into a list | |
| # of lines | |
| calltext <- unlist(strsplit(calltext, "\n", fixed = TRUE)) | |
| } | |
| # Remove leading/trailing whitespace on each line, and collapse the lines | |
| calltext <- sub("\\s+$", "", sub("^\\s+", "", calltext)) | |
| calltext <- paste(calltext, collapse=" ") | |
| # Any call text supplied is presumed UTF-8 unless we know otherwise | |
| if (Encoding(calltext) == "unknown") | |
| Encoding(calltext) <- "UTF-8" | |
| # NULL is output by R when it doesn't have an expression to output; don't | |
| # try to match it to code | |
| if (identical(calltext, "NULL")) | |
| return(c(0L, 0L, 0L, 0L, 0L, 0L)) | |
| pos <- gregexpr(calltext, singleline, fixed = TRUE)[[1]] | |
| if (length(pos) > 1) | |
| { | |
| # There is more than one instance of the call text in the function; try | |
| # to pick the first match past the preferred line. | |
| best <- which(pos > offsets[linepref]) | |
| if (length(best) == 0) | |
| { | |
| # No match past the preferred line, just pick the match closest | |
| best <- which.min(abs(linepref - pos)) | |
| } | |
| else | |
| best <- best[1] | |
| endpos <- pos[best] + attr(pos, "match.length")[best] | |
| pos <- pos[best] | |
| } | |
| else | |
| { | |
| endpos <- pos + attr(pos, "match.length") | |
| } | |
| # Return an empty source ref if we couldn't find a match | |
| if (length(pos) == 0 || pos < 0) | |
| return(c(0L, 0L, 0L, 0L, 0L, 0L)) | |
| # Compute the starting and ending lines | |
| firstline <- which(offsets >= pos, arr.ind = TRUE)[1] | |
| lastline <- which(offsets >= endpos, arr.ind = TRUE)[1] | |
| if (is.na(lastline)) | |
| lastline <- length(offsets) | |
| # Compute the starting and ending character positions within the line, | |
| # taking into account the indents we removed earlier. | |
| firstchar <- pos - (if (firstline == 1) 0 else offsets[firstline - 1]) | |
| firstchar <- firstchar + indents[firstline] | |
| # If the match is a block ({ ... }) and contains more than a few lines, | |
| # match the first line instead of the whole block; having the entire contents | |
| # of the code browser highlighted is not useful. | |
| if (substr(calltext, 1, 1) == "{" && | |
| substr(calltext, nchar(calltext), nchar(calltext)) == "}" && | |
| lastline - firstline > 5) | |
| { | |
| lastline <- firstline | |
| lastchar <- offsets[firstline] - pos | |
| } | |
| else | |
| { | |
| lastchar <- endpos - (if (lastline == 1) 0 else offsets[lastline - 1]) | |
| lastchar <- lastchar + indents[lastline] | |
| } | |
| result <- as.integer(c(firstline, firstchar, lastline, | |
| lastchar, firstchar, lastchar)) | |
| return(result) | |
| }) | |
| .rs.addFunction("functionNameFromCall", function(val) | |
| { | |
| call <- .rs.nullCoalesce( | |
| attr(val, "_rs_call", exact = TRUE), | |
| val | |
| ) | |
| if (is.function(call[[1]])) | |
| return("[Anonymous function]") | |
| if (is.name(call[[1L]])) | |
| return(as.character(call[[1L]])) | |
| .rs.deparse(call[[1L]]) | |
| }) | |
| .rs.addFunction("sanitizeCallSummary", function(object) | |
| { | |
| if (is.call(object)) | |
| { | |
| # if this is the concatenation of a large number of objects, | |
| # then just use a shorter representation of the call | |
| callee <- object[[1L]] | |
| long <- | |
| is.name(callee) && | |
| length(object) > 20 | |
| if (long) | |
| return(as.call(list(callee, quote(...)))) | |
| # sanitize each call entry separately | |
| for (i in seq_along(object)) | |
| object[[i]] <- .rs.sanitizeCallSummary(object[[i]]) | |
| # return object | |
| object | |
| } | |
| else if (is.pairlist(object)) | |
| { | |
| # handle pairlists specially, primarily because they're | |
| # used directly within function calls | |
| object | |
| } | |
| else if (!is.language(object)) | |
| { | |
| # if the object would be very expensive to deparse, | |
| # just use a placeholder instead | |
| # | |
| # note that we still want to accept literals here, | |
| # e.g. 42, "abc" | |
| if (!is.object(object) && length(object) == 1) | |
| { | |
| object | |
| } | |
| else | |
| { | |
| type <- .rs.explorer.objectType(object) | |
| as.name(sprintf("<%s>", type)) | |
| } | |
| } | |
| else | |
| { | |
| object | |
| } | |
| }) | |
| .rs.addFunction("callSummary", function(val) | |
| { | |
| call <- .rs.nullCoalesce( | |
| attr(val, "_rs_call", exact = TRUE), | |
| val | |
| ) | |
| # some calls might be very large when deparsed, especially when | |
| # they include R objects which have already been evaluted. this | |
| # happens most often with calls like: | |
| # | |
| # do.call("fn", list(object)) | |
| # | |
| # where 'object' is something very large when deparsed. we avoid | |
| # issues by replacing such objects with a short identifier of their | |
| # type / class | |
| call <- .rs.sanitizeCallSummary(call) | |
| .rs.deparse(call) | |
| }) | |
| .rs.addFunction("valueDescription", function(obj) | |
| { | |
| tryCatch( | |
| { | |
| if (missing(obj)) | |
| { | |
| return("Missing argument") | |
| } | |
| else if (is(obj, "ore.frame")) | |
| { | |
| sqlTable <- attr(obj, "sqlTable", exact = TRUE) | |
| if (is.null(sqlTable)) | |
| return("Oracle R frame") | |
| else | |
| return(paste("Oracle R frame:", sqlTable)) | |
| } | |
| else if (.rs.isExternalPointer(obj)) | |
| { | |
| class <- class(obj) | |
| if (length(class) && !identical(class, "externalptr")) | |
| { | |
| fmt <- "External pointer of class '%s'" | |
| return(sprintf(fmt, class[[1]])) | |
| } | |
| else | |
| { | |
| return("External pointer") | |
| } | |
| } | |
| else if (is.data.frame(obj)) | |
| { | |
| return(paste(dim(obj)[1], | |
| "obs. of", | |
| dim(obj)[2], | |
| ifelse(dim(obj)[2] == 1, "variable", "variables"), | |
| sep=" ")) | |
| } | |
| else if (is.environment(obj)) | |
| { | |
| return("Environment") | |
| } | |
| else if (isS4(obj)) | |
| { | |
| return(paste("Formal class ", is(obj))) | |
| } | |
| else if (is.list(obj)) | |
| { | |
| return(paste("List of ", length(obj))) | |
| } | |
| else if (is.matrix(obj) | |
| || is.numeric(obj) | |
| || is.factor(obj) | |
| || is.raw(obj) | |
| || is.character(obj) | |
| || is.logical(obj)) | |
| { | |
| return(.rs.valueFromStr(obj)) | |
| } | |
| else | |
| return("") | |
| }, | |
| error = function(e) print(e)) | |
| return ("") | |
| }) | |
| .rs.addFunction("editor", function(name, file = "", title = file, ...) | |
| { | |
| # if 'name' is missing, we're likely being invoked by | |
| # 'utils::file.edit()', so just edit the requested file | |
| if (missing(name) || is.null(name)) | |
| return(.Call("rs_editFile", file, PACKAGE = "(embedding)")) | |
| # otherwise, we're more likely being invoked by 'edit()', which | |
| # requires the parsed file to be valid R code -- so handle that | |
| # if no file has been supplied, generate one for the user | |
| if (is.null(file) || !nzchar(file)) { | |
| file <- tempfile("rstudio-scratch-", fileext = ".R") | |
| on.exit(unlink(file), add = TRUE) | |
| } | |
| # write deparsed R object to file | |
| deparsed <- if (is.function(name)) | |
| .rs.deparseFunction(name, useSource = TRUE, asString = FALSE) | |
| else | |
| deparse(name) | |
| writeLines(deparsed, con = file) | |
| # invoke edit | |
| .Call("rs_editFile", file, PACKAGE = "(embedding)") | |
| # attempt to parse-eval the generated content | |
| eval(parse(file), envir = globalenv()) | |
| }) | |
| .rs.addFunction("registerFunctionEditor", function() | |
| { | |
| options(editor = .rs.editor) | |
| }) | |
| .rs.addFunction("getSingleClass", function(obj) | |
| { | |
| class(obj)[[1L]] | |
| }) | |
| .rs.addFunction("describeObject", function(env, objName, computeSize = TRUE) | |
| { | |
| obj <- get(objName, env) | |
| if (inherits(obj, "python.builtin.object")) | |
| return(.rs.reticulate.describeObject(objName, env)) | |
| # objects containing null external pointers can crash when | |
| # evaluated--display generically (see case 4092) | |
| hasNullPtr <- .Call("rs_hasExternalPointer", obj, TRUE, PACKAGE = "(embedding)") | |
| if (hasNullPtr) | |
| { | |
| val <- "<Object with null pointer>" | |
| desc <- "An R object containing a null external pointer" | |
| size <- 0 | |
| len <- 0 | |
| } | |
| else | |
| { | |
| val <- "(unknown)" | |
| desc <- "" | |
| # some objects (e.g. ALTREP) have compact representations that are forced to materialize if | |
| # an attempt is made to compute their metrics exactly; avoid computing the size for these | |
| size <- if (computeSize) object.size(obj) else 0 | |
| len <- length(obj) | |
| } | |
| class <- .rs.getSingleClass(obj) | |
| contents <- list() | |
| contents_deferred <- FALSE | |
| # for language objects, don't evaluate, just show the expression | |
| if (is.language(obj) || is.symbol(obj)) | |
| { | |
| val <- deparse(obj) | |
| } | |
| else if (!hasNullPtr) | |
| { | |
| # for large objects (> half MB), don't try to get the value, just show | |
| # the size. Some functions (e.g. str()) can cause the object to be | |
| # copied, which is slow for large objects. | |
| if (size > 524288) | |
| { | |
| len_desc <- if (len > 1) | |
| { | |
| paste(len, " elements, ", sep = "") | |
| } | |
| else | |
| { | |
| "" | |
| } | |
| # data frames are likely to be large, but a summary is still helpful | |
| if (is.data.frame(obj)) | |
| { | |
| val <- "NO_VALUE" | |
| desc <- .rs.valueDescription(obj) | |
| } | |
| else | |
| { | |
| fmt <- "Large %s (%s %s)" | |
| val <- sprintf(fmt, class, len_desc, format(size, units = "auto", standard = "SI")) | |
| } | |
| contents_deferred <- TRUE | |
| } | |
| else | |
| { | |
| val <- .rs.valueAsString(obj) | |
| desc <- .rs.valueDescription(obj) | |
| # expandable object--supply contents | |
| if (is.list(obj) || is.data.frame(obj) || isS4(obj) || | |
| inherits(obj, c("data.table", "ore.frame", "cast_df", "xts", "DataFrame"))) | |
| { | |
| if (computeSize) | |
| { | |
| # normal object | |
| contents <- .rs.valueContents(obj) | |
| } | |
| else | |
| { | |
| # don't prefetch content for altreps | |
| val <- "NO_VALUE" | |
| contents_deferred <- TRUE | |
| } | |
| } | |
| } | |
| } | |
| list( | |
| name = .rs.scalar(objName), | |
| type = .rs.scalar(class), | |
| clazz = c(class(obj), typeof(obj)), | |
| is_data = .rs.scalar(is.data.frame(obj)), | |
| value = .rs.scalar(val), | |
| description = .rs.scalar(desc), | |
| size = .rs.scalar(size), | |
| length = .rs.scalar(len), | |
| contents = contents, | |
| contents_deferred = .rs.scalar(contents_deferred) | |
| ) | |
| }) | |
| # returns the name and frame number of an environment from a call frame | |
| .rs.addFunction("environmentCallFrameName", function(env) | |
| { | |
| numCalls <- length(sys.calls()) | |
| result <- list() | |
| for (i in 1:numCalls) | |
| { | |
| if (identical(sys.frame(i), env)) | |
| { | |
| calldesc <- paste(deparse(sys.call(i)[[1]]), "()", sep="") | |
| result <- list(name = .rs.scalar(calldesc), | |
| frame = .rs.scalar(i), | |
| local = .rs.scalar(TRUE)) | |
| break | |
| } | |
| } | |
| if (identical(result, list())) | |
| list(name = .rs.scalar("unknown"), frame = .rs.scalar(0L)) | |
| else | |
| result | |
| }) | |
| # indicate whether the given environment is local (i.e. it comes before the | |
| # global environment in the search path) | |
| .rs.addFunction("environmentIsLocal", function(env) | |
| { | |
| while (!identical(env, emptyenv())) | |
| { | |
| # if one of this environment's parents is the global environment, it's | |
| # local | |
| env = parent.env(env) | |
| if (identical(env, globalenv())) | |
| return(TRUE) | |
| } | |
| return(FALSE) | |
| }) | |
| .rs.addFunction("environmentName", function(env) | |
| { | |
| # global environment | |
| if (identical(env, globalenv())) | |
| return(".GlobalEnv") | |
| # base environment | |
| if (identical(env, baseenv())) | |
| return("package:base") | |
| # look for the environment's given name; if it doesn't have a name, check | |
| # the callstack to see if it matches the environment in one of the call | |
| # frames. | |
| result <- environmentName(env) | |
| if (nchar(result) == 0) | |
| .rs.environmentCallFrameName(env)$name | |
| else | |
| { | |
| # resolve namespaces | |
| if ((result %in% loadedNamespaces()) && | |
| identical(asNamespace(result), env)) | |
| paste("namespace:", result, sep = "") | |
| else | |
| result | |
| } | |
| }) | |
| .rs.addFunction("environmentList", function(startEnv) | |
| { | |
| env <- startEnv | |
| envs <- list() | |
| local <- TRUE | |
| # if starting above the global environment, the environments will be | |
| # unnamed. to provide sensible names for them, look for a matching frame in | |
| # the callstack. | |
| if (!identical(env, globalenv())) | |
| { | |
| while (!identical(env, globalenv()) && | |
| !identical(env, emptyenv())) | |
| { | |
| frame <- .rs.environmentCallFrameName(env) | |
| # if this frame is from the callstack, store it and proceed | |
| if (frame$frame > 0) | |
| { | |
| envs[[length(envs) + 1]] <- frame | |
| env <- parent.env(env) | |
| } | |
| # otherwise, stop here and get names normally | |
| else | |
| { | |
| break | |
| } | |
| } | |
| } | |
| # we're now past the call-frame portion of the stack; proceed normally | |
| # through the rest of the search path. | |
| while (!identical(env, emptyenv())) | |
| { | |
| # mark all environments as local until we reach the global | |
| # environment | |
| if (identical(env, globalenv())) | |
| local <- FALSE | |
| envName <- environmentName(env) | |
| # hide the RStudio internal tools environment and the autoloads | |
| # environment, and any environment that doesn't have a name | |
| if (nzchar(envName) && !envName %in% c("tools:rstudio", "Autoloads")) | |
| { | |
| envs[[length(envs) + 1]] <- list( | |
| name = .rs.scalar(envName), | |
| frame = .rs.scalar(0L), | |
| local = .rs.scalar(local) | |
| ) | |
| } | |
| env <- parent.env(env) | |
| } | |
| envs | |
| }) | |
| .rs.addFunction("removeObjects", function(objNames, env) | |
| { | |
| objects <- unlist(objNames) | |
| rm(list = objects, envir = env) | |
| }) | |
| .rs.addFunction("removeAllObjects", function(includeHidden, env) | |
| { | |
| objects <- ls(envir = env, all.names = includeHidden) | |
| rm(list = objects, envir = env) | |
| }) | |
| .rs.addFunction("getObjectContents", function(objName, env) | |
| { | |
| object <- get(objName, envir = env) | |
| .rs.valueContents(object) | |
| }) | |
| .rs.addFunction("isAltrep", function(var) | |
| { | |
| .Call("rs_isAltrep", var, PACKAGE = "(embedding)") | |
| }) | |
| .rs.addFunction("hasAltrep", function(var) | |
| { | |
| .Call("rs_hasAltrep", var, PACKAGE = "(embedding)") | |
| }) | |
| .rs.addFunction("resolveContextSourceRefs", function(callfun) | |
| { | |
| calls <- sys.calls() | |
| for (i in seq_along(calls)) | |
| { | |
| fn <- sys.function(i) | |
| if (identical(fn, callfun)) | |
| { | |
| srcref <- attr(calls[[i]], "srcref", exact = TRUE) | |
| return(srcref) | |
| } | |
| } | |
| NULL | |
| }) | |