|
|
- ## NOTE ON S3 METHODS: New S3 methods are not automatically registered. You can
- ## register them manually after you have inserted method_name.my_class into your
- ## package environment using ess-developer, like follows:
- ##
- ## registerS3method("method_name", "my_class", my_package:::method_name.my_class)
- ##
- ## If an S3 methods already exists in a package, ESS-developer will do the right
- ## thing.
-
- ## evaluate the STRING by saving into a file and calling .ess.ns_source
- .ess.ns_eval <- function(string, visibly, output, package,
- file = tempfile("ESSDev"), verbose = FALSE,
- fallback_env = NULL, local_env = parent.frame()) {
- cat(string, file = file)
- on.exit(.ess.file.remove(file))
- .ess.ns_source(file, visibly, output, package = package,
- verbose = verbose, fake.source = TRUE,
- fallback_env = fallback_env, local_env = local_env)
- }
-
- ##' Source FILE into an environment. After having a look at each new object in
- ##' the environment, decide what to do with it. Handles plain objects,
- ##' functions, existing S3 methods, S4 classes and methods.
- ##' @param fallback_env environment to assign objects which don't exist in the
- ##' package namespace
- .ess.ns_source <- function(file, visibly, output, expr,
- package = "", verbose = FALSE,
- fake.source = FALSE,
- fallback_env = NULL,
- local_env = NULL) {
- oldopts <- options(warn = 2)
- on.exit(options(oldopts))
- pname <- paste("package:", package, sep = "")
- envpkg <- tryCatch(as.environment(pname), error = function(cond) NULL)
- if (is.null(envpkg))
- if (require(package, quietly = TRUE, character.only = TRUE)) {
- envpkg <- tryCatch(as.environment(pname), error = function(cond) NULL)
- } else {
- ## no such package; source in current (local) user environment
- return(.ess.source(file, visibly = visibly,
- output = output, local = local_env,
- fake.source = fake.source))
- }
-
- envns <- tryCatch(asNamespace(package), error = function(cond) NULL)
- if (is.null(envns))
- stop(gettextf("Can't find a namespace environment corresponding to package name '%s\"",
- package), domain = NA)
-
- ## Here we know that both envns and envpkg exists and are environments
- if (is.null(fallback_env))
- fallback_env <- .ess.ns_insert_essenv(envns)
-
- ## Get all Imports envs where we propagate objects
- pkgEnvNames <- Filter(.ess.is_package, search())
- packages <- lapply(pkgEnvNames, function(envName) substring(envName, 9))
- importsEnvs <- lapply(packages, function(pkgName) parent.env(asNamespace(pkgName)))
-
- ## Evaluate the FILE into new ENV
- env <- .ess.ns_evalSource(file, visibly, output, substitute(expr), package, fake.source)
- envPackage <- getPackageName(env, FALSE)
- if (nzchar(envPackage) && envPackage != package)
- warning(gettextf("Supplied package, %s, differs from package inferred from source, %s",
- sQuote(package), sQuote(envPackage)), domain = NA)
-
- ## Get all sourced objects, methods and classes
- allObjects <- objects(envir = env, all.names = TRUE)
- allObjects <- allObjects[!(allObjects %in% c(".cacheOnAssign", ".packageName"))]
- MetaPattern <- methods:::.TableMetaPattern()
- ClassPattern <- methods:::.ClassMetaPattern()
- allPlainObjects <- allObjects[!(grepl(MetaPattern, allObjects) |
- grepl(ClassPattern, allObjects))]
- allMethodTables <- allObjects[grepl(MetaPattern, allObjects)]
- allClassDefs <- allObjects[grepl(ClassPattern, allObjects)]
-
- ## PLAIN OBJECTS and FUNCTIONS:
- funcNs <- funcPkg <- newFunc <- newNs <- newObjects <- newPkg <- objectsNs <- objectsPkg <- character()
- dependentPkgs <- list()
-
- for (this in allPlainObjects) {
- thisEnv <- get(this, envir = env)
- thisNs <- NULL
-
- ## NS
- if (exists(this, envir = envns, inherits = FALSE)){
- thisNs <- get(this, envir = envns)
- if(is.function(thisNs) || is.function(thisEnv)){
- if(is.function(thisNs) && is.function(thisEnv)){
- if(.ess.differs(thisEnv, thisNs)){
- environment(thisEnv) <- environment(thisNs)
- .ess.assign(this, thisEnv, envns)
- funcNs <- c(funcNs, this)
- if(exists(".__S3MethodsTable__.", envir = envns, inherits = FALSE)){
- S3_table <- get(".__S3MethodsTable__.", envir = envns)
- if(exists(this, envir = S3_table, inherits = FALSE))
- .ess.assign(this, thisEnv, S3_table)
- }
- }
- }else{
- newNs <- c(newNs, this)
- }
- }else{
- if(!identical(thisEnv, thisNs)){
- .ess.assign(this, thisEnv, envns)
- objectsNs <- c(objectsNs, this)
- }
- }
- }else{
- newNs <- c(newNs, this)
- }
-
- ## PKG
- if (exists(this, envir = envpkg, inherits = FALSE)){
- thisPkg <- get(this, envir = envpkg)
- if(is.function(thisPkg) || is.function(thisEnv)){
- if(is.function(thisPkg) && is.function(thisEnv)){
- if(.ess.differs(thisPkg, thisEnv)){
- environment(thisEnv) <- environment(thisPkg)
- .ess.assign(this, thisEnv, envpkg)
- funcPkg <- c(funcPkg, this)
- }
- }else{
- newPkg <- c(newPkg, this)
- }
- }else{
- if(!identical(thisPkg, thisEnv)){
- .ess.assign(this, thisEnv, envpkg)
- objectsPkg <- c(objectsPkg, this)
- }
- }
- }else{
- newPkg <- c(newPkg, this)
- }
-
- if (!is.null(thisNs)) {
- isDependent <- .ess.ns_propagate(thisEnv, this, importsEnvs)
- newDeps <- stats::setNames(list(packages[isDependent]), this)
- dependentPkgs <- c(dependentPkgs, newDeps)
- }
- }
-
- ## deal with new plain objects and functions
- for (this in intersect(newPkg, newNs)) {
- thisEnv <- get(this, envir = env, inherits = FALSE)
- if (exists(this, envir = fallback_env, inherits = FALSE)){
- thisGl <- get(this, envir = fallback_env)
- if (.ess.differs(thisEnv, thisGl)) {
- if (is.function(thisEnv)) {
- environment(thisEnv) <- envns
- newFunc <- c(newFunc, this)
- } else {
- newObjects <- c(newObjects, this)
- }
- .ess.assign(this, thisEnv, fallback_env)
- if (.is.essenv(fallback_env))
- .ess.assign(this, thisEnv, .GlobalEnv)
- }
- } else {
- if (is.function(thisEnv)) {
- environment(thisEnv) <- envns
- newFunc <- c(newFunc, this)
- } else {
- newObjects <- c(newObjects, this)
- }
- .ess.assign(this, thisEnv, fallback_env)
- if (.is.essenv(fallback_env))
- .ess.assign(this, thisEnv, .GlobalEnv)
- }
- }
-
- if(length(funcNs))
- objectsNs <- c(objectsNs, sprintf("FUN[%s]", paste(funcNs, collapse = ", ")))
- if(length(funcPkg))
- objectsPkg <- c(objectsPkg, sprintf("FUN[%s]", paste(funcPkg, collapse = ", ")))
- if(length(newFunc))
- newObjects <- c(newObjects, sprintf("FUN[%s]", paste(newFunc, collapse = ", ")))
-
- ## CLASSES
- classesPkg <- classesNs <- newClasses <- character()
- for(this in allClassDefs){
- newPkg <- newNs <- FALSE
- thisEnv <- get(this, envir = env)
- if(exists(this, envir = envpkg, inherits = FALSE)){
- if(!.ess.identicalClass(thisEnv, get(this, envir = envpkg))){
- .ess.assign(this, thisEnv, envir = envpkg)
- classesPkg <- c(classesPkg, this)
- }
- }else{
- newPkg <- TRUE
- }
- if(exists(this, envir = envns, inherits = FALSE)){
- if(!.ess.identicalClass(thisEnv, get(this, envir = envns))){
- .ess.assign(this, thisEnv, envir = envns)
- classesNs <- c(classesNs, this)
- }
- }else{
- newNs <- TRUE
- }
- if(newNs && newPkg){
- if(exists(this, envir = fallback_env, inherits = FALSE)){
- if(!.ess.identicalClass(thisEnv, get(this, envir = fallback_env))){
- .ess.assign(this, thisEnv, envir = fallback_env)
- newClasses <- c(newClasses, this)
- }
- }else{
- .ess.assign(this, thisEnv, envir = fallback_env)
- newClasses <- c(newClasses, this)
- }
- }
- }
- if(length(classesPkg))
- objectsPkg <- gettextf("CLS[%s]", sub(ClassPattern, "", paste(classesPkg, collapse = ", ")))
- if(length(classesNs))
- objectsNs <- gettextf("CLS[%s]", sub(ClassPattern, "", paste(classesNs, collapse = ", ")))
- if(length(newClasses))
- newObjects <- gettextf("CLS[%s]", sub(ClassPattern, "", paste(newClasses, collapse = ", ")))
-
- ## METHODS:
- ## Method internals: For efficiency reasons setMethod() caches
- ## method definition into a global table which you can get with
- ## 'getMethodsForDispatch' function, and when a method is dispatched that
- ## table is used. When ess-developer is used to source method definitions the
- ## two copies of the functions are identical up to the environment. The
- ## environment of the cached object has namespace:foo as it's parent but the
- ## environment of the object in local table is precisely namspace:foo. This
- ## does not cause any difference in evaluation.
- methodNames <- allMethodTables
- methods <- sub(methods:::.TableMetaPrefix(), "", methodNames)
- methods <- sub(":.*", "", methods)
- methodsNs <- newMethods <- character()
- for (i in seq_along(methods)){
- table <- methodNames[[i]]
- tableEnv <- get(table, envir = env)
- if(exists(table, envir = envns, inherits = FALSE)){
- inserted <- .ess.ns_insertMethods(tableEnv, get(table, envir = envns), envns)
- if(length(inserted))
- methodsNs <- c(methodsNs, gettextf("%s{%s}", methods[[i]], paste(inserted, collapse = ", ")))
- }else if(exists(table, envir = fallback_env, inherits = FALSE)){
- inserted <- .ess.ns_insertMethods(tableEnv, get(table, envir = fallback_env), envns)
- if(length(inserted))
- newMethods <- c(newMethods, gettextf("%s{%s}", methods[[i]], paste(inserted, collapse = ", ")))
- }else{
- .ess.assign(table, tableEnv, envir = fallback_env)
- newMethods <- c(newMethods, gettextf("%s{%s}", methods[[i]], paste(objects(envir = tableEnv, all.names = T), collapse = ", ")))
- }
- }
- if(length(methodsNs))
- objectsNs <- c(objectsNs, gettextf("METH[%s]", paste(methodsNs, collapse = ", ")))
- if(length(newMethods))
- newObjects <- c(newObjects, gettextf("METH[%s]", paste(newMethods, collapse = ", ")))
-
- if (verbose) {
- msgs <- unlist(list(
- if(length(objectsPkg))
- sprintf("PKG: %s", paste(objectsPkg, collapse = ", ")),
- if(length(objectsNs))
- sprintf("NS: %s", paste(objectsNs, collapse = ", ")),
- if(length(dependentPkgs))
- .ess.ns_format_deps(dependentPkgs),
- if(length(newObjects)) {
- env_name <- .ess.ns_env_name(fallback_env)
- sprintf("%s: %s", env_name, paste(newObjects, collapse = ", "))
- }))
- if(length(msgs))
- .ess_mpi_message(paste(msgs, collapse = " "))
-
- }
-
- invisible(env)
- }
-
- .ess.ns_insertMethods <- function(tableEnv, tablePkg, envns) {
- inserted <- character()
- for(m in ls(envir = tableEnv, all.names = T)){
- if(exists(m, envir = tablePkg, inherits = FALSE)){
- thisEnv <- get(m, envir = tableEnv)
- thisPkg <- get(m, envir = tablePkg)
- if(is(thisEnv, "MethodDefinition") && is(thisPkg, "MethodDefinition") &&
- .ess.differs(thisEnv@.Data, thisPkg@.Data)){
- environment(thisEnv@.Data) <- envns
- ## environment of cached method in getMethodsForDispatch table is still env
- ## not a problem as such, but might confuse users
- .ess.assign(m, thisEnv, tablePkg)
- inserted <- c(inserted, m)
- }}}
- inserted
- }
-
- ## our version of R's evalSource
- .ess.ns_evalSource <- function(file, visibly, output, expr, package = "",
- fake.source = FALSE) {
- envns <- tryCatch(asNamespace(package), error = function(cond) NULL)
- if(is.null(envns))
- stop(gettextf("Package \"%s\" is not attached and no namespace found for it",
- package), domain = NA)
- env <- new.env(parent = envns)
- env[[".packageName"]] <- package
- methods:::setCacheOnAssign(env, TRUE)
- if (missing(file))
- eval(expr, envir = env)
- else if (is(file, "character"))
- for (f in file) {
- .ess.source(f, local = env, visibly = visibly,
- output = output, keep.source = TRUE,
- max.deparse.length = 300,
- fake.source = fake.source,
- message.prefix = sprintf("[%s] ", package))
- }
- else stop(gettextf("Invalid file argument: got an object of class \"%s\"",
- class(file)[[1]]), domain = NA)
- env
- }
-
- .ess.assign <- function(x, value, envir) {
- ## Cannot add bindings to locked environments
- exists <- exists(x, envir = envir, inherits = FALSE)
- if (exists && bindingIsLocked(x, envir)) {
- unlockBinding(x, envir)
- assign(x, value, envir = envir, inherits = FALSE)
- op <- options(warn = -1)
- on.exit(options(op))
- lockBinding(x, envir)
- } else if (exists || !environmentIsLocked(envir)) {
- assign(x, value, envir = envir, inherits = FALSE)
- } else {
- warning(sprintf("Cannot assign `%s` in locked environment", x),
- call. = FALSE)
- }
- invisible(NULL)
- }
-
- .ess.identicalClass <- function(cls1, cls2, printInfo = FALSE) {
- slots1 <- slotNames(class(cls1))
- slots2 <- slotNames(class(cls2))
- if(identical(slots1, slots2)){
- vK <- grep("versionKey", slots1)
- if(length(vK))
- slots1 <- slots2 <- slots1[-vK]
- out <- sapply(slots1, function(nm) identical(slot(cls1, nm), slot(cls2, nm)))
- if(printInfo) print(out)
- all(out)
- }
- }
-
- .ess.differs <- function(f1, f2) {
- if (is.function(f1) && is.function(f2)){
- !(identical(body(f1), body(f2)) && identical(args(f1), args(f2)))
- }else
- !identical(f1, f2)
- }
-
- .ess.is_package <- function(envName) {
- isPkg <- identical(substring(envName, 0, 8), "package:")
- isPkg && (envName != "package:base")
- }
-
- .ess.ns_propagate <- function(obj, name, importsEnvs) {
- containsObj <- vapply(importsEnvs, logical(1), FUN = function(envs) {
- name %in% names(envs)
- })
-
- lapply(importsEnvs[containsObj], .ess.assign,
- x = name, value = obj)
-
- containsObj
- }
-
-
- .ess.ns_format_deps <- function(dependentPkgs) {
- pkgs <- unique(unlist(dependentPkgs, use.names = FALSE))
-
- lapply(pkgs, function(pkg) {
- isDep <- vapply(dependentPkgs, function(deps) pkg %in% deps, logical(1))
- pkgDependentObjs <- names(dependentPkgs[isDep])
- sprintf("DEP:%s [%s] ", pkg, paste(pkgDependentObjs, collapse = ", "))
- })
- }
-
- .ess.ns_env_name <- function(env) {
- name <- environmentName(env)
- name <-
- if (name == "") "Local"
- else if (grepl("^essenv:", name)) "NEW"
- else name
- name
- }
-
- .ess.ns_insert_essenv <- function(nsenv) {
- if (is.character(nsenv))
- nsenv <- base::asNamespace(nsenv)
- stopifnot(isNamespace(nsenv))
- if (identical(nsenv, .BaseNamespaceEnv))
- return(.GlobalEnv)
- essenv_name <- sprintf("essenv:%s", environmentName(nsenv))
- nsenv_parent <- parent.env(nsenv)
- if (environmentName(nsenv_parent) == essenv_name) {
- return(nsenv_parent)
- }
- essenv <- new.env(parent = nsenv_parent)
- essenv[[".__ESSENV__."]] <- TRUE
- attr(essenv, "name") <- essenv_name
- nssym <- ".__NAMESPACE__."
- nssym_val <- get(nssym, envir = nsenv, inherits = FALSE)
- unlockBinding(nssym, nsenv)
- nsenv[[nssym]] <- NULL
- on.exit({
- nsenv[[nssym]] <- nssym_val
- lockBinding(nssym, nsenv)
- })
- parent.env(nsenv) <- essenv
- essenv
- }
-
- .is.essenv <- function(env) {
- exists(".__ESSENV__.", envir = env, inherits = FALSE)
- }
|