blob: 853bf5b4f01f2430b17d4a45bef8945d56f77a13 [file] [log] [blame]
# File src/library/base/R/namespace.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2019 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
## give the base namespace a table for registered methods
`.__S3MethodsTable__.` <- new.env(hash = TRUE, parent = baseenv())
## NOTA BENE:
## 1) This code should work also when methods is not yet loaded
## 2) We use ':::' instead of '::' inside the code below, for efficiency only
getNamespace <- function(name) {
ns <- .Internal(getRegisteredNamespace(name))
if (! is.null(ns)) ns
else loadNamespace(name)
}
.getNamespace <- function(name) .Internal(getRegisteredNamespace(name))
..getNamespace <- function(name, where) {
ns <- .Internal(getRegisteredNamespace(name))
if (!is.null(ns)) ns
else tryCatch(loadNamespace(name),
error = function(e) {
warning(gettextf("namespace %s is not available and has been replaced\nby .GlobalEnv when processing object %s",
sQuote(name)[1L], sQuote(where)),
domain = NA, call. = FALSE, immediate. = TRUE)
.GlobalEnv
})
}
loadedNamespaces <- function() names(.Internal(getNamespaceRegistry()))
isNamespaceLoaded <- function(name) .Internal(isRegisteredNamespace(name))
getNamespaceName <- function(ns) {
ns <- asNamespace(ns)
if (isBaseNamespace(ns)) "base"
else .getNamespaceInfo(ns, "spec")["name"]
}
getNamespaceVersion <- function(ns) {
ns <- asNamespace(ns)
if (isBaseNamespace(ns))
c(version = paste(R.version$major, R.version$minor, sep = "."))
else .getNamespaceInfo(ns, "spec")["version"]
}
getNamespaceExports <- function(ns) {
ns <- asNamespace(ns)
names(if(isBaseNamespace(ns)) .BaseNamespaceEnv
else .getNamespaceInfo(ns, "exports"))
}
getNamespaceImports <- function(ns) {
ns <- asNamespace(ns)
if (isBaseNamespace(ns)) NULL
else .getNamespaceInfo(ns, "imports")
}
getNamespaceUsers <- function(ns) {
nsname <- getNamespaceName(asNamespace(ns))
users <- character()
for (n in loadedNamespaces()) {
inames <- names(getNamespaceImports(n))
if (match(nsname, inames, 0L))
users <- c(n, users)
}
users
}
getExportedValue <- function(ns, name) {
ns <- asNamespace(ns)
if (isBaseNamespace(ns))
get(name, envir = ns, inherits = FALSE) # incl. error
else {
if (!is.null(oNam <- .getNamespaceInfo(ns, "exports")[[name]])) {
get0(oNam, envir = ns)
} else { ## <pkg> :: <dataset> for lazydata :
ld <- .getNamespaceInfo(ns, "lazydata")
if (!is.null(obj <- ld[[name]]))
obj
else { ## if there's a lazydata object with value NULL:
if(exists(name, envir = ld, inherits = FALSE))
NULL
else
stop(gettextf("'%s' is not an exported object from 'namespace:%s'",
name, getNamespaceName(ns)),
call. = FALSE, domain = NA)
}
}
}
}
`::` <- function(pkg, name) {
pkg <- as.character(substitute(pkg))
name <- as.character(substitute(name))
getExportedValue(pkg, name)
}
## NOTE: Both "::" and ":::" must signal an error for non existing objects
`:::` <- function(pkg, name) {
pkg <- as.character(substitute(pkg))
name <- as.character(substitute(name))
get(name, envir = asNamespace(pkg), inherits = FALSE)
}
attachNamespace <- function(ns, pos = 2L, depends = NULL, exclude, include.only)
{
## only used to run .onAttach
runHook <- function(hookname, env, libname, pkgname) {
if (!is.null(fun <- env[[hookname]])) {
res <- tryCatch(fun(libname, pkgname), error = identity)
if (inherits(res, "error")) {
stop(gettextf("%s failed in %s() for '%s', details:\n call: %s\n error: %s",
hookname, "attachNamespace", nsname,
deparse(conditionCall(res))[1L],
conditionMessage(res)),
call. = FALSE, domain = NA)
}
}
## else if (exists(".First.lib", envir = env, inherits = FALSE) &&
## nsname == Sys.getenv("R_INSTALL_PKG"))
## warning(sprintf("ignoring .First.lib() for package %s",
## sQuote(nsname)), domain = NA, call. = FALSE)
}
runUserHook <- function(pkgname, pkgpath) {
hook <- getHook(packageEvent(pkgname, "attach")) # might be list()
for(fun in hook) try(fun(pkgname, pkgpath))
}
ns <- asNamespace(ns, base.OK = FALSE)
nsname <- getNamespaceName(ns)
nspath <- .getNamespaceInfo(ns, "path")
attname <- paste0("package:", nsname)
if (attname %in% search())
stop("namespace is already attached")
env <- attach(NULL, pos = pos, name = attname)
## we do not want to run e.g. .onDetach here
on.exit(.Internal(detach(pos)))
attr(env, "path") <- nspath
exports <- getNamespaceExports(ns)
importIntoEnv(env, exports, ns, exports)
## always exists, might be empty
dimpenv <- .getNamespaceInfo(ns, "lazydata")
dnames <- names(dimpenv)
.Internal(importIntoEnv(env, dnames, dimpenv, dnames))
if(length(depends) > 0L) env$.Depends <- depends
Sys.setenv("_R_NS_LOAD_" = nsname)
on.exit(Sys.unsetenv("_R_NS_LOAD_"), add = TRUE)
runHook(".onAttach", ns, dirname(nspath), nsname)
## adjust variables for 'exclude', 'include.only' arguments
if (! missing(exclude) && length(exclude) > 0)
rm(list = exclude, envir = env)
if (! missing(include.only)) {
vars <- ls(env, all.names = TRUE)
nf <- setdiff(include.only, vars)
if (length(nf) > 0) {
nf <- strwrap(paste(nf, collapse = ", "),
indent = 4L, exdent = 4L)
stop(gettextf("not found in namespace %s: \n\n%s\n",
sQuote(nsname), nf),
call. = FALSE, domain = NA)
}
rm(list = setdiff(vars, include.only), envir = env)
}
lockEnvironment(env, TRUE)
runUserHook(nsname, nspath)
on.exit()
Sys.unsetenv("_R_NS_LOAD_")
invisible(env)
}
## *inside* another function, useful to check for cycles
dynGet <- function(x, ifnotfound = stop(gettextf("%s not found",
sQuote(x)), domain = NA),
minframe = 1L, inherits = FALSE)
{
n <- sys.nframe()
myObj <- structure(list(.b = as.raw(7)), foo = 47L)# "very improbable" object
while (n > minframe) {
n <- n - 1L
env <- sys.frame(n)
r <- get0(x, envir = env, inherits=inherits, ifnotfound = myObj)
if(!identical(r, myObj))
return(r)
}
ifnotfound
}
loadNamespace <- function (package, lib.loc = NULL,
keep.source = getOption("keep.source.pkgs"),
partial = FALSE, versionCheck = NULL,
keep.parse.data = getOption("keep.parse.data.pkgs"))
{
libpath <- attr(package, "LibPath")
package <- as.character(package)[[1L]]
loading <- dynGet("__NameSpacesLoading__", NULL)
if (match(package, loading, 0L))
stop("cyclic namespace dependency detected when loading ",
sQuote(package), ", already loading ",
paste(sQuote(loading), collapse = ", "),
domain = NA)
"__NameSpacesLoading__" <- c(package, loading)
ns <- .Internal(getRegisteredNamespace(package))
if (! is.null(ns)) {
if(!is.null(zop <- versionCheck[["op"]]) &&
!is.null(zversion <- versionCheck[["version"]])) {
current <- getNamespaceVersion(ns)
if(!do.call(zop, list(as.numeric_version(current), zversion)))
stop(gettextf("namespace %s %s is already loaded, but %s %s is required",
sQuote(package), current, zop, zversion),
domain = NA)
}
ns
} else {
## only used here for .onLoad
runHook <- function(hookname, env, libname, pkgname) {
if (!is.null(fun <- env[[hookname]])) {
res <- tryCatch(fun(libname, pkgname), error = identity)
if (inherits(res, "error")) {
stop(gettextf("%s failed in %s() for '%s', details:\n call: %s\n error: %s",
hookname, "loadNamespace", pkgname,
deparse(conditionCall(res))[1L],
conditionMessage(res)),
call. = FALSE, domain = NA)
}
}
}
runUserHook <- function(pkgname, pkgpath) {
hooks <- getHook(packageEvent(pkgname, "onLoad")) # might be list()
for(fun in hooks) try(fun(pkgname, pkgpath))
}
makeNamespace <- function(name, version = NULL, lib = NULL) {
impenv <- new.env(parent = .BaseNamespaceEnv, hash = TRUE)
attr(impenv, "name") <- paste0("imports:", name)
env <- new.env(parent = impenv, hash = TRUE)
name <- as.character(as.name(name))
version <- as.character(version)
info <- new.env(hash = TRUE, parent = baseenv())
env$.__NAMESPACE__. <- info
info$spec <- c(name = name, version = version)
setNamespaceInfo(env, "exports", new.env(hash = TRUE, parent = baseenv()))
dimpenv <- new.env(parent = baseenv(), hash = TRUE)
attr(dimpenv, "name") <- paste0("lazydata:", name)
setNamespaceInfo(env, "lazydata", dimpenv)
setNamespaceInfo(env, "imports", list("base" = TRUE))
## this should be an absolute path
setNamespaceInfo(env, "path",
normalizePath(file.path(lib, name), "/", TRUE))
setNamespaceInfo(env, "dynlibs", NULL)
## <FIXME delayed S3 method registration>
setNamespaceInfo(env, "S3methods", matrix(NA_character_, 0L, 4L))
## </FIXME delayed S3 method registration>
env$.__S3MethodsTable__. <-
new.env(hash = TRUE, parent = baseenv())
.Internal(registerNamespace(name, env))
env
}
sealNamespace <- function(ns) {
namespaceIsSealed <- function(ns)
environmentIsLocked(ns)
ns <- asNamespace(ns, base.OK = FALSE)
if (namespaceIsSealed(ns))
stop(gettextf("namespace %s is already sealed in 'loadNamespace'",
sQuote(getNamespaceName(ns))),
call. = FALSE, domain = NA)
lockEnvironment(ns, TRUE)
lockEnvironment(parent.env(ns), TRUE)
}
addNamespaceDynLibs <- function(ns, newlibs) {
dynlibs <- .getNamespaceInfo(ns, "dynlibs")
setNamespaceInfo(ns, "dynlibs", c(dynlibs, newlibs))
}
bindTranslations <- function(pkgname, pkgpath)
{
## standard packages are treated differently
std <- c("compiler", "foreign", "grDevices", "graphics", "grid",
"methods", "parallel", "splines", "stats", "stats4",
"tcltk", "tools", "utils")
popath <- if (pkgname %in% std) .popath else file.path(pkgpath, "po")
if(!file.exists(popath)) return()
bindtextdomain(pkgname, popath)
bindtextdomain(paste0("R-", pkgname), popath)
}
assignNativeRoutines <- function(dll, lib, env, nativeRoutines) {
if(length(nativeRoutines) == 0L) return(character())
varnames <- character()
symnames <- character()
if(nativeRoutines$useRegistration) {
## Use the registration information to register ALL the symbols
fixes <- nativeRoutines$registrationFixes
routines <- getDLLRegisteredRoutines.DLLInfo(dll, addNames = FALSE)
lapply(routines,
function(type) {
lapply(type,
function(sym) {
varName <- paste0(fixes[1L], sym$name, fixes[2L])
if(exists(varName, envir = env, inherits = FALSE))
warning(gettextf(
"failed to assign RegisteredNativeSymbol for %s to %s since %s is already defined in the %s namespace",
sym$name, varName, varName, sQuote(package)),
domain = NA, call. = FALSE)
else {
env[[varName]] <- sym
varnames <<- c(varnames,
varName)
symnames <<- c(symnames,
sym$name)
}
})
})
}
symNames <- nativeRoutines$symbolNames
if(length(symNames)) {
symbols <- getNativeSymbolInfo(symNames, dll, unlist = FALSE,
withRegistrationInfo = TRUE)
lapply(seq_along(symNames),
function(i) {
## could vectorize this outside of the loop
## and assign to different variable to
## maintain the original names.
varName <- names(symNames)[i]
origVarName <- symNames[i]
if(exists(varName, envir = env, inherits = FALSE))
if(origVarName != varName)
warning(gettextf(
"failed to assign NativeSymbolInfo for %s to %s since %s is already defined in the %s namespace",
origVarName, varName, varName, sQuote(package)),
domain = NA, call. = FALSE)
else
warning(gettextf(
"failed to assign NativeSymbolInfo for %s since %s is already defined in the %s namespace",
origVarName, varName, sQuote(package)),
domain = NA, call. = FALSE)
else {
assign(varName, symbols[[origVarName]],
envir = env)
varnames <<- c(varnames, varName)
symnames <<- c(symnames, origVarName)
}
})
}
names(symnames) <- varnames
symnames
}
## find package, allowing a calling handler to retry if not found.
## could move the retry functionality into find.package.
fp.lib.loc <- c(libpath, lib.loc)
pkgpath <- find.package(package, fp.lib.loc, quiet = TRUE)
if (length(pkgpath) == 0L) {
cond <- packageNotFoundError(package, fp.lib.loc, sys.call())
withRestarts(stop(cond), retry_loadNamespace = function() NULL)
pkgpath <- find.package(package, fp.lib.loc, quiet = TRUE)
if (length(pkgpath) == 0L)
stop(cond)
}
bindTranslations(package, pkgpath)
package.lib <- dirname(pkgpath)
package <- basename(pkgpath) # need the versioned name
if (! packageHasNamespace(package, package.lib)) {
hasNoNamespaceError <-
function (package, package.lib, call = NULL) {
class <- c("hasNoNamespaceError", "error", "condition")
msg <- gettextf("package %s does not have a namespace",
sQuote(package))
structure(list(message = msg, package = package,
package.lib = package.lib, call = call),
class = class)
}
stop(hasNoNamespaceError(package, package.lib))
}
## create namespace; arrange to unregister on error
## Can we rely on the existence of R-ng 'nsInfo.rds' and
## 'package.rds'?
## No, not during builds of standard packages
## stats4 depends on methods, but exports do not matter
## whilst it is being built
nsInfoFilePath <- file.path(pkgpath, "Meta", "nsInfo.rds")
nsInfo <- if(file.exists(nsInfoFilePath)) readRDS(nsInfoFilePath)
else parseNamespaceFile(package, package.lib, mustExist = FALSE)
pkgInfoFP <- file.path(pkgpath, "Meta", "package.rds")
if(file.exists(pkgInfoFP)) {
pkgInfo <- readRDS(pkgInfoFP)
version <- pkgInfo$DESCRIPTION["Version"]
vI <- pkgInfo$Imports
if(is.null(built <- pkgInfo$Built))
stop(gettextf("package %s has not been installed properly\n",
sQuote(basename(pkgpath))),
call. = FALSE, domain = NA)
R_version_built_under <- as.numeric_version(built$R)
if(R_version_built_under < "3.0.0")
stop(gettextf("package %s was built before R 3.0.0: please re-install it",
sQuote(basename(pkgpath))),
call. = FALSE, domain = NA)
## we need to ensure that S4 dispatch is on now if the package
## will require it, or the exports will be incomplete.
dependsMethods <- "methods" %in% names(pkgInfo$Depends)
if(dependsMethods) loadNamespace("methods")
if(!is.null(zop <- versionCheck[["op"]]) &&
!is.null(zversion <- versionCheck[["version"]]) &&
!do.call(zop, list(as.numeric_version(version), zversion)))
stop(gettextf("namespace %s %s is being loaded, but %s %s is required",
sQuote(package), version, zop, zversion),
domain = NA)
}
## moved from library in R 3.4.0
checkLicense <- function(pkg, pkgInfo, pkgPath)
{
L <- tools:::analyze_license(pkgInfo$DESCRIPTION["License"])
if(!L$is_empty && !L$is_verified) {
site_file <-
path.expand(file.path(R.home("etc"), "licensed.site"))
if(file.exists(site_file) &&
pkg %in% readLines(site_file)) return()
personal_file <- path.expand("~/.R/licensed")
if(file.exists(personal_file)) {
agreed <- readLines(personal_file)
if(pkg %in% agreed) return()
} else agreed <- character()
if(!interactive())
stop(gettextf(
"package %s has a license that you need to accept in an interactive session",
sQuote(pkg)), domain = NA)
lfiles <- file.path(pkgpath, c("LICENSE", "LICENCE"))
lfiles <- lfiles[file.exists(lfiles)]
if(length(lfiles)) {
message(gettextf(
"package %s has a license that you need to accept after viewing",
sQuote(pkg)), domain = NA)
readline("press RETURN to view license")
encoding <- pkgInfo$DESCRIPTION["Encoding"]
if(is.na(encoding)) encoding <- ""
## difR and EVER have a Windows' 'smart quote' LICEN[CS]E file
if(encoding == "latin1") encoding <- "cp1252"
file.show(lfiles[1L], encoding = encoding)
} else {
message(gettextf(paste("package %s has a license that you need to accept:",
"according to the DESCRIPTION file it is",
"%s", sep="\n"),
sQuote(pkg),
pkgInfo$DESCRIPTION["License"]), domain = NA)
}
choice <- utils::menu(c("accept", "decline"),
title = paste("License for", sQuote(pkg)))
if(choice != 1)
stop(gettextf("license for package %s not accepted",
sQuote(package)), domain = NA, call. = FALSE)
dir.create(dirname(personal_file), showWarnings=FALSE)
writeLines(c(agreed, pkg), personal_file)
}
}
## avoid any bootstrapping issues by these exemptions
if(!package %in% c("datasets", "grDevices", "graphics", "methods",
"stats", "tools", "utils") &&
isTRUE(getOption("checkPackageLicense", FALSE)))
checkLicense(package, pkgInfo, pkgpath)
## Check that the internals version used to build this package
## matches the version of current R. Failure in this test
## should only occur if the R version is an unreleased devel
## version or the package was build with an unrelease devel
## version. Other mismatches should be caught earlier by the
## version checks.
## Meta will not exist when first building tools,
## so pkgInfo was not created above.
if(dir.exists(file.path(pkgpath, "Meta"))) {
ffile <- file.path(pkgpath, "Meta", "features.rds")
features <- if (file.exists(ffile)) readRDS(ffile) else NULL
needsComp <- as.character(pkgInfo$DESCRIPTION["NeedsCompilation"])
if (identical(needsComp, "yes") ||
file.exists(file.path(pkgpath, "libs"))) {
internalsID <- features$internalsID
if (is.null(internalsID))
## the initial internalsID for packages installed
## prior to introducing features.rds in the meta data
internalsID <- "0310d4b8-ccb1-4bb8-ba94-d36a55f60262"
if (internalsID != .Internal(internalsID()))
stop(gettextf("package %s was installed by an R version with different internals; it needs to be reinstalled for use with this R version",
sQuote(package)), call. = FALSE, domain = NA)
}
}
ns <- makeNamespace(package, version = version, lib = package.lib)
on.exit(.Internal(unregisterNamespace(package)))
## process imports
for (i in nsInfo$imports) {
if (is.character(i))
namespaceImport(ns,
loadNamespace(i, c(lib.loc, .libPaths()),
versionCheck = vI[[i]]),
from = package)
else if (!is.null(i$except))
namespaceImport(ns,
loadNamespace(j <- i[[1L]],
c(lib.loc, .libPaths()),
versionCheck = vI[[j]]),
from = package,
except = i$except)
else
namespaceImportFrom(ns,
loadNamespace(j <- i[[1L]],
c(lib.loc, .libPaths()),
versionCheck = vI[[j]]),
i[[2L]], from = package)
}
for(imp in nsInfo$importClasses)
namespaceImportClasses(ns, loadNamespace(j <- imp[[1L]],
c(lib.loc, .libPaths()),
versionCheck = vI[[j]]),
imp[[2L]], from = package)
for(imp in nsInfo$importMethods)
namespaceImportMethods(ns, loadNamespace(j <- imp[[1L]],
c(lib.loc, .libPaths()),
versionCheck = vI[[j]]),
imp[[2L]], from = package)
## store info for loading namespace for loadingNamespaceInfo to read
"__LoadingNamespaceInfo__" <- list(libname = package.lib,
pkgname = package)
env <- asNamespace(ns)
## save the package name in the environment
env$.packageName <- package
## load the code
codename <- strsplit(package, "_", fixed = TRUE)[[1L]][1L]
codeFile <- file.path(pkgpath, "R", codename)
if (file.exists(codeFile)) {
# The code file has been converted to the native encoding
save.enc <- options(encoding = "native.enc")
res <- try(sys.source(codeFile, env, keep.source = keep.source,
keep.parse.data = keep.parse.data))
options(save.enc)
if(inherits(res, "try-error"))
stop(gettextf("unable to load R code in package %s",
sQuote(package)), call. = FALSE, domain = NA)
}
# a package without R code currently is required to have a namespace
# else warning(gettextf("package %s contains no R code",
# sQuote(package)), call. = FALSE, domain = NA)
## partial loading stops at this point
## -- used in preparing for lazy-loading
if (partial) return(ns)
## lazy-load any sysdata
dbbase <- file.path(pkgpath, "R", "sysdata")
if (file.exists(paste0(dbbase, ".rdb"))) lazyLoad(dbbase, env)
## load any lazydata into a separate environment
dbbase <- file.path(pkgpath, "data", "Rdata")
if(file.exists(paste0(dbbase, ".rdb")))
lazyLoad(dbbase, .getNamespaceInfo(env, "lazydata"))
## register any S3 methods
registerS3methods(nsInfo$S3methods, package, env)
## load any dynamic libraries
dlls <- list()
dynLibs <- nsInfo$dynlibs
nativeRoutines <- list()
for (i in seq_along(dynLibs)) {
lib <- dynLibs[i]
dlls[[lib]] <- library.dynam(lib, package, package.lib)
routines <- assignNativeRoutines(dlls[[lib]], lib, env,
nsInfo$nativeRoutines[[lib]])
nativeRoutines[[lib]] <- routines
## If the DLL has a name as in useDynLib(alias = foo),
## then assign DLL reference to alias. Check if
## names() is NULL to handle case that the nsInfo.rds
## file was created before the names were added to the
## dynlibs vector.
if(!is.null(names(nsInfo$dynlibs))
&& nzchar(names(nsInfo$dynlibs)[i]))
env[[names(nsInfo$dynlibs)[i]]] <- dlls[[lib]]
setNamespaceInfo(env, "DLLs", dlls)
}
addNamespaceDynLibs(env, nsInfo$dynlibs)
setNamespaceInfo(env, "nativeRoutines", nativeRoutines)
## used in e.g. utils::assignInNamespace
Sys.setenv("_R_NS_LOAD_" = package)
on.exit(Sys.unsetenv("_R_NS_LOAD_"), add = TRUE)
## run the load hook
runHook(".onLoad", env, package.lib, package)
## process exports, seal, and clear on.exit action
exports <- nsInfo$exports
for (p in nsInfo$exportPatterns)
exports <- c(ls(env, pattern = p, all.names = TRUE), exports)
##
if(.isMethodsDispatchOn() && methods:::.hasS4MetaData(ns) &&
!identical(package, "methods") ) {
## cache generics, classes in this namespace (but not methods itself,
## which pre-cached at install time
methods::cacheMetaData(ns, TRUE, ns)
## This also ran .doLoadActions
## load actions may have added objects matching patterns
for (p in nsInfo$exportPatterns) {
expp <- ls(ns, pattern = p, all.names = TRUE)
newEx <- !(expp %in% exports)
if(any(newEx))
exports <- c(expp[newEx], exports)
}
## process class definition objects
expClasses <- nsInfo$exportClasses
##we take any pattern, but check to see if the matches are classes
pClasses <- character()
aClasses <- methods::getClasses(ns)
classPatterns <- nsInfo$exportClassPatterns
## defaults to exportPatterns
if(!length(classPatterns))
classPatterns <- nsInfo$exportPatterns
pClasses <- unique(unlist(lapply(classPatterns, grep, aClasses,
value=TRUE)))
if( length(pClasses) ) {
good <- vapply(pClasses, methods::isClass, NA, where = ns)
if( !any(good) && length(nsInfo$exportClassPatterns))
warning(gettextf(
"'exportClassPattern' specified in 'NAMESPACE' but no matching classes in package %s",
sQuote(package)),
call. = FALSE, domain = NA)
expClasses <- c(expClasses, pClasses[good])
}
if(length(expClasses)) {
missingClasses <-
!vapply(expClasses, methods::isClass, NA, where = ns)
if(any(missingClasses))
stop(gettextf("in package %s classes %s were specified for export but not defined",
sQuote(package),
paste(expClasses[missingClasses],
collapse = ", ")),
domain = NA)
expClasses <- paste0(methods::classMetaName(""), expClasses)
}
## process methods metadata explicitly exported or
## implied by exporting the generic function.
allGenerics <- unique(c(methods:::.getGenerics(ns),
methods:::.getGenerics(parent.env(ns))))
expMethods <- nsInfo$exportMethods
## check for generic functions corresponding to exported methods
addGenerics <- expMethods[is.na(match(expMethods, exports))]
if(length(addGenerics)) {
nowhere <- vapply(addGenerics, function(what) !exists(what, mode = "function", envir = ns),
NA, USE.NAMES=FALSE)
if(any(nowhere)) {
warning(gettextf("no function found corresponding to methods exports from %s for: %s",
sQuote(package),
paste(sQuote(sort(unique(addGenerics[nowhere]))), collapse = ", ")),
domain = NA, call. = FALSE)
addGenerics <- addGenerics[!nowhere]
}
if(length(addGenerics)) {
## skip primitives
addGenerics <- addGenerics[vapply(addGenerics, function(what)
!is.primitive(get(what, mode = "function", envir = ns)), NA)]
## the rest must be generic functions, implicit or local
## or have been cached via a DEPENDS package
ok <- vapply(addGenerics, methods:::.findsGeneric, 1L, ns)
if(!all(ok)) {
bad <- sort(unique(addGenerics[!ok]))
msg <-
ngettext(length(bad),
"Function found when exporting methods from the namespace %s which is not S4 generic: %s",
"Functions found when exporting methods from the namespace %s which are not S4 generic: %s")
stop(sprintf(msg, sQuote(package),
paste(sQuote(bad), collapse = ", ")),
domain = NA, call. = FALSE)
}
else if(any(ok > 1L)) #from the cache, don't add
addGenerics <- addGenerics[ok < 2L]
}
### <note> Uncomment following to report any local generic functions
### that should have been exported explicitly. But would be reported
### whenever the package is loaded, which is not when it is relevant.
### </note>
## local <- sapply(addGenerics, function(what) identical(as.character(get(what, envir = ns)@package), package))
## if(any(local))
## message(gettextf("export(%s) from package %s generated by exportMethods()",
## paste(addGenerics[local], collapse = ", ")),
## domain = NA)
exports <- c(exports, addGenerics)
}
expTables <- character()
if(length(allGenerics)) {
expMethods <-
unique(c(expMethods,
exports[!is.na(match(exports, allGenerics))]))
missingMethods <- !(expMethods %in% allGenerics)
if(any(missingMethods))
stop(gettextf("in %s methods for export not found: %s",
sQuote(package),
paste(expMethods[missingMethods],
collapse = ", ")),
domain = NA)
tPrefix <- methods:::.TableMetaPrefix()
allMethodTables <-
unique(c(methods:::.getGenerics(ns, tPrefix),
methods:::.getGenerics(parent.env(ns), tPrefix)))
needMethods <-
(exports %in% allGenerics) & !(exports %in% expMethods)
if(any(needMethods))
expMethods <- c(expMethods, exports[needMethods])
## Primitives must have their methods exported as long
## as a global table is used in the C code to dispatch them:
## The following keeps the exported files consistent with
## the internal table.
pm <- allGenerics[!(allGenerics %in% expMethods)]
if(length(pm)) {
prim <- vapply(pm, function(pmi) {
f <- methods::getFunction(pmi, FALSE,
FALSE, ns)
is.primitive(f)
}, logical(1L))
expMethods <- c(expMethods, pm[prim])
}
for(i in seq_along(expMethods)) {
mi <- expMethods[[i]]
if(!(mi %in% exports) &&
exists(mi, envir = ns, mode = "function",
inherits = FALSE))
exports <- c(exports, mi)
pattern <- paste0(tPrefix, mi, ":")
ii <- grep(pattern, allMethodTables, fixed = TRUE)
if(length(ii)) {
if(length(ii) > 1L) {
warning(gettextf("multiple methods tables found for %s",
sQuote(mi)), call. = FALSE, domain = NA)
ii <- ii[1L]
}
expTables[[i]] <- allMethodTables[ii]
}
else { ## but not possible?
warning(gettextf("failed to find metadata object for %s",
sQuote(mi)), call. = FALSE, domain = NA)
}
}
}
else if(length(expMethods))
stop(gettextf("in package %s methods %s were specified for export but not defined",
sQuote(package),
paste(expMethods, collapse = ", ")),
domain = NA)
exports <- unique(c(exports, expClasses, expTables))
}
## certain things should never be exported.
if (length(exports)) {
stoplist <- c(".__NAMESPACE__.", ".__S3MethodsTable__.",
".packageName", ".First.lib", ".onLoad",
".onAttach", ".conflicts.OK", ".noGenerics")
exports <- exports[! exports %in% stoplist]
}
namespaceExport(ns, exports)
sealNamespace(ns)
runUserHook(package, pkgpath)
on.exit()
Sys.unsetenv("_R_NS_LOAD_")
ns
}
}
## A version which returns TRUE/FALSE
requireNamespace <- function (package, ..., quietly = FALSE)
{
package <- as.character(package)[[1L]] # like loadNamespace
ns <- .Internal(getRegisteredNamespace(package))
res <- TRUE
if (is.null(ns)) {
if(!quietly)
packageStartupMessage(gettextf("Loading required namespace: %s",
package), domain = NA)
value <- tryCatch(loadNamespace(package, ...), error = function(e) e)
if (inherits(value, "error")) {
if (!quietly) {
msg <- conditionMessage(value)
cat("Failed with error: ",
sQuote(msg), "\n", file = stderr(), sep = "")
.Internal(printDeferredWarnings())
}
res <- FALSE
}
}
invisible(res)
}
loadingNamespaceInfo <- function() {
dynGet("__LoadingNamespaceInfo__", stop("not loading a namespace"))
}
topenv <- function(envir = parent.frame(),
matchThisEnv = getOption("topLevelEnvironment")) {
.Internal(topenv(envir, matchThisEnv))
}
unloadNamespace <- function(ns)
{
## check, so we do not load & unload:
if ((is.character(ns) && any(ns == loadedNamespaces())) ||
(is.environment(ns) && any(getNamespaceName(ns) == loadedNamespaces()))) {
## only used to run .onUnload
runHook <- function(hookname, env, ...) {
if (!is.null(fun <- env[[hookname]])) {
res <- tryCatch(fun(...), error=identity)
if (inherits(res, "error")) {
warning(gettextf("%s failed in %s() for '%s', details:\n call: %s\n error: %s",
hookname, "unloadNamespace", nsname,
deparse(conditionCall(res))[1L],
conditionMessage(res)),
call. = FALSE, domain = NA)
}
}
}
ns <- asNamespace(ns, base.OK = FALSE)
nsname <- getNamespaceName(ns)
pos <- match(paste0("package:", nsname), search())
if (! is.na(pos)) detach(pos = pos)
users <- getNamespaceUsers(ns)
if (length(users))
stop(gettextf("namespace %s is imported by %s so cannot be unloaded",
sQuote(getNamespaceName(ns)),
paste(sQuote(users), collapse = ", ")),
domain = NA)
nspath <- .getNamespaceInfo(ns, "path")
hook <- getHook(packageEvent(nsname, "onUnload")) # might be list()
for(fun in rev(hook)) try(fun(nsname, nspath))
runHook(".onUnload", ns, nspath)
.Internal(unregisterNamespace(nsname))
if(.isMethodsDispatchOn() && methods:::.hasS4MetaData(ns))
methods::cacheMetaData(ns, FALSE, ns)
.Internal(lazyLoadDBflush(paste0(nspath, "/R/", nsname, ".rdb")))
}
invisible()
}
isNamespace <- function(ns) .Internal(isNamespaceEnv(ns))
isBaseNamespace <- function(ns) identical(ns, .BaseNamespaceEnv)
getNamespaceInfo <- function(ns, which) {
ns <- asNamespace(ns, base.OK = FALSE)
get(which, envir = ns[[".__NAMESPACE__."]])
}
.getNamespaceInfo <- function(ns, which) {
ns[[".__NAMESPACE__."]][[which]]
}
setNamespaceInfo <- function(ns, which, val) {
ns <- asNamespace(ns, base.OK = FALSE)
info <- ns[[".__NAMESPACE__."]]
info[[which]] <- val
}
asNamespace <- function(ns, base.OK = TRUE) {
if (is.character(ns) || is.name(ns))
ns <- getNamespace(ns)
if (! isNamespace(ns))
stop("not a namespace")
else if (! base.OK && isBaseNamespace(ns))
stop("operation not allowed on base namespace")
else ns
}
namespaceImport <- function(self, ..., from = NULL, except = character(0L))
for (ns in list(...))
namespaceImportFrom(self, asNamespace(ns), from = from,
except = except)
namespaceImportFrom <- function(self, ns, vars, generics, packages,
from = "non-package environment",
except = character(0L))
{
addImports <- function(ns, from, what) {
imp <- structure(list(what), names = getNamespaceName(from))
imports <- getNamespaceImports(ns)
setNamespaceInfo(ns, "imports", c(imports, imp))
}
namespaceIsSealed <- function(ns)
environmentIsLocked(ns)
makeImportExportNames <- function(spec) {
old <- as.character(spec)
new <- names(spec)
if (is.null(new)) new <- old
else {
change <- !nzchar(new)
new[change] <- old[change]
}
names(old) <- new
old
}
whichMethodMetaNames <- function(impvars) {
if(!.isMethodsDispatchOn())
return(numeric())
seq_along(impvars)[startsWith(impvars, ".__T__")]
}
genericPackage <- function(f) {
if(methods::is(f, "genericFunction")) f@package
else if(is.primitive(f)) "base"
else "<unknown>"
}
if (is.character(self))
self <- getNamespace(self)
ns <- asNamespace(ns)
nsname <- getNamespaceName(ns)
impvars <- if (missing(vars)) {
## certain things should never be imported:
## but most of these are never exported (exception: .Last.lib)
stoplist <- c(".__NAMESPACE__.", ".__S3MethodsTable__.",
".packageName", ".First.lib", ".Last.lib",
".onLoad", ".onAttach", ".onDetach",
".conflicts.OK", ".noGenerics")
vars <- getNamespaceExports(ns)
vars <- vars[! vars %in% stoplist]
} else vars
impvars <- impvars[! impvars %in% except]
impvars <- makeImportExportNames(impvars)
impnames <- names(impvars)
if (anyDuplicated(impnames)) {
stop(gettextf("duplicate import names %s",
paste(sQuote(impnames[duplicated(impnames)]),
collapse = ", ")), domain = NA)
}
if (isNamespace(self)) {
if(isBaseNamespace(self)) {
impenv <- self
msg <- gettext("replacing local value with import %s when loading %s")
register <- FALSE
}
else {
if (namespaceIsSealed(self))
stop("cannot import into a sealed namespace")
impenv <- parent.env(self)
msg <- gettext("replacing previous import by %s when loading %s")
register <- TRUE
}
}
else if (is.environment(self)) {
impenv <- self
msg <- gettext("replacing local value with import %s when loading %s")
register <- FALSE
}
else stop("invalid import target")
which <- whichMethodMetaNames(impvars)
if(length(which)) {
## If methods are already in impenv, merge and don't import
delete <- integer()
for(i in which) {
methodsTable <- .mergeImportMethods(impenv, ns, impvars[[i]])
if(is.null(methodsTable))
{} ## first encounter, just import it
else { ##
delete <- c(delete, i)
if(!missing(generics)) {
genName <- generics[[i]]
## if(i > length(generics) || !nzchar(genName))
## {warning("got invalid index for importing ",mlname); next}
fdef <- methods::getGeneric(genName,
where = impenv,
package = packages[[i]])
if(is.null(fdef))
warning(gettextf("found methods to import for function %s but not the generic itself",
sQuote(genName)),
call. = FALSE, domain = NA)
else
methods:::.updateMethodsInTable(fdef, ns, TRUE)
}
}
}
if(length(delete)) {
impvars <- impvars[-delete]
impnames <- impnames[-delete]
}
}
for (n in impnames)
if (!is.null(genImp <- impenv[[n]])) {
if (.isMethodsDispatchOn() && methods::isGeneric(n, ns)) {
## warn only if generic overwrites a function which
## it was not derived from
genNs <- genericPackage(get(n, envir = ns))
if(identical(genNs, genericPackage(genImp))) next # same generic
genImpenv <- environmentName(environment(genImp))
## May call environment() on a non-function--an undocumented
## "feature" of environment() is that it returns a special
## attribute for non-functions, usually NULL
if (!identical(genNs, genImpenv) ||
methods::isGeneric(n, impenv)) {}
else next
}
if (identical(genImp, get(n, ns))) next
if (isNamespace(self) && !isBaseNamespace(self)) {
## Now try to figure out where we imported from
## The 'imports' list is named by where-from
## and is in order of adding.
current <- getNamespaceInfo(self, "imports")
poss <- lapply(rev(current), "[", n)
poss <- poss[!sapply(poss, is.na)]
if(length(poss) >= 1L) {
msg <- gettext("replacing previous import %s by %s when loading %s")
prev <- names(poss)[1L]
warning(sprintf(msg,
sQuote(paste(prev, n, sep = "::")),
sQuote(paste(nsname, n, sep = "::")),
sQuote(from)),
call. = FALSE, domain = NA)
} else
warning(sprintf(msg, sQuote(paste(nsname, n, sep = "::")),
sQuote(from)),
call. = FALSE, domain = NA)
} else {
## this is always called from another function,
## so reporting call is unhelpful
warning(sprintf(msg, sQuote(paste(nsname, n, sep = "::")),
sQuote(from)),
call. = FALSE, domain = NA)
}
}
importIntoEnv(impenv, impnames, ns, impvars)
if (register)
addImports(self, ns, if (missing(vars)) TRUE else impvars)
}
namespaceImportClasses <- function(self, ns, vars, from = NULL)
{
for(i in seq_along(vars))
vars[[i]] <- methods::classMetaName(vars[[i]])
namespaceImportFrom(self, asNamespace(ns), vars, from = from)
}
namespaceImportMethods <- function(self, ns, vars, from = NULL)
{
allVars <- character()
generics <- character()
packages <- character()
allFuns <- methods:::.getGenerics(ns) # all the methods tables in ns
allPackages <- attr(allFuns, "package")
pkg <- methods::getPackageName(ns)
found <- vars %in% allFuns
if(!all(found)) {
message(sprintf(ngettext(sum(!found),
"No methods found in package %s for request: %s when loading %s",
"No methods found in package %s for requests: %s when loading %s"),
sQuote(pkg),
paste(sQuote(vars[!found]), collapse = ", "),
sQuote(getNamespaceName(self))),
domain = NA)
vars <- vars[found]
}
found <- vars %in% allFuns
if(!all(found))
stop(sprintf(ngettext(sum(!found),
"requested method not found in environment/package %s: %s when loading %s",
"requested methods not found in environment/package %s: %s when loading %s"),
sQuote(pkg),
paste(sQuote(vars[!found]), collapse = ", "),
sQuote(getNamespaceName(self))),
call. = FALSE, domain = NA)
for(i in seq_along(allFuns)) {
## import methods tables if asked for
## or if the corresponding generic was imported
g <- allFuns[[i]]
p <- allPackages[[i]]
if(exists(g, envir = self, inherits = FALSE) # already imported
|| g %in% vars) { # requested explicitly
tbl <- methods:::.TableMetaName(g, p)
if(is.null(.mergeImportMethods(self, ns, tbl))) { # a new methods table
allVars <- c(allVars, tbl) # import it;else, was merged
generics <- c(generics, g)
packages <- c(packages, p)
}
}
if(g %in% vars && !exists(g, envir = self, inherits = FALSE)) {
if(!is.null(f <- get0(g, envir = ns)) && methods::is(f, "genericFunction")) {
allVars <- c(allVars, g)
generics <- c(generics, g)
packages <- c(packages, p)
} else if (g %in% c("as.vector", "is.unsorted", "unlist")) {
## implicit generics
} else { # should be primitive
fun <- methods::getFunction(g, mustFind = FALSE, where = self)
if(is.primitive(fun) || methods::is(fun, "genericFunction")) {}
else
warning(gettextf(
"No generic function %s found corresponding to requested imported methods from package %s when loading %s (malformed exports?)",
sQuote(g), sQuote(pkg), sQuote(from)),
domain = NA, call. = FALSE)
}
}
}
namespaceImportFrom(self, asNamespace(ns), allVars, generics, packages,
from = from)
}
importIntoEnv <- function(impenv, impnames, expenv, expnames) {
exports <- getNamespaceInfo(expenv, "exports")
ex <- names(exports)
if(!all(eie <- expnames %in% ex)) {
miss <- expnames[!eie]
## if called (indirectly) for namespaceImportClasses
## these are all classes
if(all(startsWith(miss, ".__C__"))) {
miss <- sub("^\\.__C__", "", miss)
stop(sprintf(ngettext(length(miss),
"class %s is not exported by 'namespace:%s'",
"classes %s are not exported by 'namespace:%s'"),
paste(paste0('"', miss, '"'), collapse = ", "),
getNamespaceName(expenv)),
call. = FALSE, domain = NA)
} else {
stop(sprintf(ngettext(length(miss),
"object %s is not exported by 'namespace:%s'",
"objects %s are not exported by 'namespace:%s'"),
paste(sQuote(miss), collapse = ", "),
getNamespaceName(expenv)),
call. = FALSE, domain = NA)
}
}
expnames <- unlist(mget(expnames, envir = exports, inherits = FALSE), recursive=FALSE)
if (is.null(impnames)) impnames <- character()
if (is.null(expnames)) expnames <- character()
.Internal(importIntoEnv(impenv, impnames, expenv, expnames))
}
namespaceExport <- function(ns, vars) {
namespaceIsSealed <- function(ns)
environmentIsLocked(ns)
if (namespaceIsSealed(ns))
stop("cannot add to exports of a sealed namespace")
ns <- asNamespace(ns, base.OK = FALSE)
if (length(vars)) {
addExports <- function(ns, new) {
exports <- .getNamespaceInfo(ns, "exports")
expnames <- names(new)
objs <- names(exports)
ex <- expnames %in% objs
if(any(ex))
warning(sprintf(ngettext(sum(ex),
"previous export '%s' is being replaced",
"previous exports '%s' are being replaced"),
paste(sQuote(expnames[ex]), collapse = ", ")),
call. = FALSE, domain = NA)
list2env(as.list(new), exports)
}
makeImportExportNames <- function(spec) {
old <- as.character(spec)
new <- names(spec)
if (is.null(new)) new <- old
else {
change <- !nzchar(new)
new[change] <- old[change]
}
names(old) <- new
old
}
new <- makeImportExportNames(unique(vars))
## calling exists each time is too slow, so do two phases
undef <- new[! new %in% names(ns)]
undef <- undef[! vapply(undef, exists, NA, envir = ns)]
if (length(undef)) {
undef <- do.call("paste", as.list(c(undef, sep = ", ")))
stop(gettextf("undefined exports: %s", undef), domain = NA)
}
if(.isMethodsDispatchOn()) .mergeExportMethods(new, ns)
addExports(ns, new)
}
}
.mergeExportMethods <- function(new, ns)
{
## avoid bootstrapping issues when using methods:::methodsPackageMetaName("M","")
## instead of ".__M__" :
newMethods <- new[startsWith(new, ".__M__")]
nsimports <- parent.env(ns)
for(what in newMethods) {
if(!is.null(m1 <- nsimports[[what]])) {
m2 <- get(what, envir = ns)
ns[[what]] <- methods::mergeMethods(m1, m2)
}
}
}
packageHasNamespace <- function(package, package.lib)
file.exists(file.path(package.lib, package, "NAMESPACE"))
parseNamespaceFile <- function(package, package.lib, mustExist = TRUE)
{
namespaceFilePath <- function(package, package.lib)
file.path(package.lib, package, "NAMESPACE")
## These two functions are essentially local to the parsing of
## the namespace file and don't need to be made available to
## users. These manipulate the data from useDynLib() directives
## for the same DLL to determine how to map the symbols to R
## variables.
nativeRoutineMap <-
## Creates a new NativeRoutineMap.
function(useRegistration, symbolNames, fixes) {
proto <- list(useRegistration = FALSE,
symbolNames = character())
class(proto) <- "NativeRoutineMap"
mergeNativeRoutineMaps(proto, useRegistration, symbolNames, fixes)
}
mergeNativeRoutineMaps <-
## Merges new settings into a NativeRoutineMap
function(map, useRegistration, symbolNames, fixes) {
if(!useRegistration)
names(symbolNames) <-
paste0(fixes[1L], names(symbolNames), fixes[2L])
else
map$registrationFixes <- fixes
map$useRegistration <- map$useRegistration || useRegistration
map$symbolNames <- c(map$symbolNames, symbolNames)
map
}
nsFile <- namespaceFilePath(package, package.lib)
descfile <- file.path(package.lib, package, "DESCRIPTION")
enc <- if (file.exists(descfile)) {
read.dcf(file = descfile, "Encoding")[1L]
} else NA_character_
if (file.exists(nsFile))
directives <- if (!is.na(enc) &&
! Sys.getlocale("LC_CTYPE") %in% c("C", "POSIX")) {
lines <- readLines(nsFile, warn = FALSE)
tmp <- iconv(lines, from = enc, to = "")
bad <- which(is.na(tmp))
## do not report purely comment lines,
comm <- grep("^[[:space:]]*#", lines[bad],
invert = TRUE, useBytes = TRUE)
if(length(bad[comm]))
stop("unable to re-encode some lines in NAMESPACE file")
tmp <- iconv(lines, from = enc, to = "", sub = "byte")
con <- textConnection(tmp)
on.exit(close(con))
parse(con, keep.source = FALSE, srcfile = NULL)
} else parse(nsFile, keep.source = FALSE, srcfile = NULL)
else if (mustExist)
stop(gettextf("package %s has no 'NAMESPACE' file", sQuote(package)),
domain = NA)
else directives <- NULL
exports <- character()
exportPatterns <- character()
exportClasses <- character()
exportClassPatterns <- character()
exportMethods <- character()
imports <- list()
importMethods <- list()
importClasses <- list()
dynlibs <- character()
nS3methods <- 1000L
## <FIXME delayed S3 method registration>
S3methods <- matrix(NA_character_, nS3methods, 4L)
## </FIXME delayed S3 method registration>
nativeRoutines <- list()
nS3 <- 0L
parseDirective <- function(e) {
## trying to get more helpful error message:
asChar <- function(cc) {
r <- as.character(cc)
if(any(r == ""))
stop(gettextf("empty name in directive '%s' in 'NAMESPACE' file",
as.character(e[[1L]])),
domain = NA)
r
}
evalToChar <- function(cc) {
vars <- all.vars(cc)
names(vars) <- vars
as.character(eval(eval(call("substitute", cc, as.list(vars))),
.GlobalEnv))
}
switch(as.character(e[[1L]]),
"if" = if (eval(e[[2L]], .GlobalEnv))
parseDirective(e[[3L]])
else if (length(e) == 4L)
parseDirective(e[[4L]]),
"{" = for (ee in as.list(e[-1L])) parseDirective(ee),
"=" =,
"<-" = {
parseDirective(e[[3L]])
if(as.character(e[[3L]][[1L]]) == "useDynLib")
names(dynlibs)[length(dynlibs)] <<- asChar(e[[2L]])
},
export = {
exp <- e[-1L]
exp <- structure(asChar(exp), names = names(exp))
exports <<- c(exports, exp)
},
exportPattern = {
pat <- asChar(e[-1L])
exportPatterns <<- c(pat, exportPatterns)
},
exportClassPattern = {
pat <- asChar(e[-1L])
exportClassPatterns <<- c(pat, exportClassPatterns)
},
exportClass = , exportClasses = {
exportClasses <<- c(asChar(e[-1L]), exportClasses)
},
exportMethods = {
exportMethods <<- c(asChar(e[-1L]), exportMethods)
},
import = {
except <- e$except
e$except <- NULL
pkgs <- as.list(asChar(e[-1L]))
if (!is.null(except)) {
pkgs <- lapply(pkgs, list, except=evalToChar(except))
}
imports <<- c(imports, pkgs)
},
importFrom = {
imp <- e[-1L]
ivars <- imp[-1L]
inames <- names(ivars)
imp <- list(asChar(imp[1L]),
structure(asChar(ivars), names = inames))
imports <<- c(imports, list(imp))
},
importClassFrom = , importClassesFrom = {
imp <- asChar(e[-1L])
pkg <- imp[[1L]]
impClasses <- imp[-1L]
imp <- list(asChar(pkg), asChar(impClasses))
importClasses <<- c(importClasses, list(imp))
},
importMethodsFrom = {
imp <- asChar(e[-1L])
pkg <- imp[[1L]]
impMethods <- imp[-1L]
imp <- list(asChar(pkg), asChar(impMethods))
importMethods <<- c(importMethods, list(imp))
},
useDynLib = {
## This attempts to process as much of the
## information as possible when NAMESPACE is parsed
## rather than when it is loaded and creates
## NativeRoutineMap objects to handle the mapping
## of symbols to R variable names.
## The name is the second element after useDynLib
dyl <- as.character(e[2L])
## We ensure uniqueness at the end.
dynlibs <<-
structure(c(dynlibs, dyl),
names = c(names(dynlibs),
ifelse(!is.null(names(e)) &&
nzchar(names(e)[2L]), names(e)[2L], "" )))
if (length(e) > 2L) {
## Author has specified some mappings for the symbols
symNames <- as.character(e[-c(1L, 2L)])
names(symNames) <- names(e[-c(1, 2)])
## If there are no names, then use the names of
## the symbols themselves.
if (length(names(symNames)) == 0L)
names(symNames) <- symNames
else if (any(w <- names(symNames) == "")) {
names(symNames)[w] <- symNames[w]
}
## For each DLL, we build up a list the (R
## variable name, symbol name) mappings. We do
## this in a NativeRoutineMap object and we
## merge potentially multiple useDynLib()
## directives for the same DLL into a single
## map. Then we have separate NativeRoutineMap
## for each different DLL. E.g. if we have
## useDynLib(foo, a, b, c) and useDynLib(bar,
## a, x, y) we would maintain and resolve them
## separately.
dup <- duplicated(names(symNames))
if (any(dup))
warning(gettextf("duplicate symbol names %s in useDynLib(\"%s\")",
paste(sQuote(names(symNames)[dup]),
collapse = ", "), dyl),
domain = NA, call. = FALSE)
symNames <- symNames[!dup]
## Deal with any prefix/suffix pair.
fixes <- c("", "")
idx <- match(".fixes", names(symNames))
if(!is.na(idx)) {
## Take .fixes and treat it as a call,
## e.g. c("pre", "post") or a regular name
## as the prefix.
if(nzchar(symNames[idx])) {
e <- parse(text = symNames[idx],
keep.source = FALSE,
srcfile = NULL)[[1L]]
if(is.call(e))
val <- eval(e, .GlobalEnv)
else
val <- as.character(e)
if(length(val))
fixes[seq_along(val)] <- val
}
symNames <- symNames[-idx]
}
## Deal with a .registration entry. It must be
## .registration = value and value will be coerced
## to a logical.
useRegistration <- FALSE
idx <- match(".registration", names(symNames))
if(!is.na(idx)) {
useRegistration <- as.logical(symNames[idx])
symNames <- symNames[-idx]
}
## Now merge into the NativeRoutineMap.
nativeRoutines[[ dyl ]] <<-
if(dyl %in% names(nativeRoutines))
mergeNativeRoutineMaps(nativeRoutines[[ dyl ]],
useRegistration,
symNames, fixes)
else
nativeRoutineMap(useRegistration, symNames,
fixes)
}
},
S3method = {
spec <- e[-1L]
if (length(spec) != 2L && length(spec) != 3L)
stop(gettextf("bad 'S3method' directive: %s",
deparse(e)),
call. = FALSE, domain = NA)
nS3 <<- nS3 + 1L
if(nS3 > nS3methods) {
old <- S3methods
nold <- nS3methods
nS3methods <<- nS3methods * 2L
## <FIXME delayed S3 method registration>
new <- matrix(NA_character_, nS3methods, 4L)
## </FIXME delayed S3 method registration>
ind <- seq_len(nold)
## <FIXME delayed S3 method registration>
for (i in 1:4) new[ind, i] <- old[ind, i]
## </FIXME delayed S3 method registration>
S3methods <<- new
rm(old, new)
}
## <FIXME delayed S3 method registration>
if(is.call(gen <- spec[[1L]]) &&
identical(as.character(gen[[1L]]), "::")) {
pkg <- as.character(gen[[2L]])[1L]
gen <- as.character(gen[[3L]])[1L]
S3methods[nS3, c(seq_along(spec), 4L)] <<-
c(gen, asChar(spec[-1L]), pkg)
} else
## </FIXME delayed S3 method registration>
S3methods[nS3, seq_along(spec)] <<- asChar(spec)
},
stop(gettextf("unknown namespace directive: %s", deparse(e, nlines=1L)),
call. = FALSE, domain = NA)
)
}
for (e in directives)
parseDirective(e)
## need to preserve the names on dynlibs, so unique() is not appropriate.
dynlibs <- dynlibs[!duplicated(dynlibs)]
list(imports = imports, exports = exports,
exportPatterns = unique(exportPatterns),
importClasses = importClasses, importMethods = importMethods,
exportClasses = unique(exportClasses),
exportMethods = unique(exportMethods),
exportClassPatterns = unique(exportClassPatterns),
dynlibs = dynlibs, nativeRoutines = nativeRoutines,
S3methods = unique(S3methods[seq_len(nS3), , drop = FALSE]) )
} ## end{parseNamespaceFile}
## Still used inside registerS3methods().
registerS3method <- function(genname, class, method, envir = parent.frame()) {
addNamespaceS3method <- function(ns, generic, class, method) {
regs <- rbind(.getNamespaceInfo(ns, "S3methods"),
## <FIXME delayed S3 method registration>
c(generic, class, method, NA_character_))
## </FIXME delayed S3 method registration>
setNamespaceInfo(ns, "S3methods", regs)
}
groupGenerics <- c("Math", "Ops", "Summary", "Complex")
defenv <- if(genname %in% groupGenerics) .BaseNamespaceEnv
else {
genfun <- get(genname, envir = envir)
if(.isMethodsDispatchOn() && methods::is(genfun, "genericFunction"))
genfun <- methods::finalDefaultMethod(genfun@default)
if (typeof(genfun) == "closure") environment(genfun)
else .BaseNamespaceEnv
}
if (is.null(table <- defenv[[".__S3MethodsTable__."]])) {
table <- new.env(hash = TRUE, parent = baseenv())
defenv[[".__S3MethodsTable__."]] <- table
}
if (is.character(method)) {
assignWrapped <- function(x, method, home, envir) {
method <- method # force evaluation
home <- home # force evaluation
delayedAssign(x, get(method, envir = home), assign.env = envir)
}
if(!exists(method, envir = envir)) {
## need to avoid conflict with message at l.1298
warning(gettextf("S3 method %s was declared but not found",
sQuote(method)), call. = FALSE)
} else {
assignWrapped(paste(genname, class, sep = "."), method, home = envir,
envir = table)
}
}
else if (is.function(method))
assign(paste(genname, class, sep = "."), method, envir = table)
else stop("bad method")
if (isNamespace(envir) && ! identical(envir, .BaseNamespaceEnv))
addNamespaceS3method(envir, genname, class, method)
}
registerS3methods <- function(info, package, env)
{
n <- NROW(info)
if(n == 0L) return()
assignWrapped <- function(x, method, home, envir) {
method <- method # force evaluation
home <- home # force evaluation
delayedAssign(x, get(method, envir = home), assign.env = envir)
}
overwrite <- matrix(NA_character_, 0, 2)
.registerS3method <- function(genname, class, method, nm, envir)
{
## S3 generics should either be imported explicitly or be in
## the base namespace, so we start the search at the imports
## environment, parent.env(envir), which is followed by the
## base namespace. (We have already looked in the namespace.)
## However, in case they have not been imported, we first
## look up where some commonly used generics are (including the
## group generics).
defenv <- if(!is.na(w <- .knownS3Generics[genname])) asNamespace(w)
else {
if(is.null(genfun <- get0(genname, envir = parent.env(envir))))
stop(gettextf("object '%s' not found whilst loading namespace '%s'",
genname, package), call. = FALSE, domain = NA)
if(.isMethodsDispatchOn() && methods::is(genfun, "genericFunction"))
genfun <- genfun@default # nearly always, the S3 generic
if (typeof(genfun) == "closure") environment(genfun)
else .BaseNamespaceEnv
}
if (is.null(table <- defenv[[".__S3MethodsTable__."]])) {
table <- new.env(hash = TRUE, parent = baseenv())
defenv[[".__S3MethodsTable__."]] <- table
}
if(!is.null(e <- table[[nm]]) &&
!identical(e, get(method, envir = envir))) {
current <- environmentName(environment(e))
overwrite <<- rbind(overwrite, c(as.vector(nm), current))
}
assignWrapped(nm, method, home = envir, envir = table)
}
methname <- paste(info[,1], info[,2], sep = ".")
z <- is.na(info[,3])
info[z,3] <- methname[z]
## <FIXME delayed S3 method registration>
## Simpler to re-arrange so that packages for delayed registration
## come in the last column, and the non-delayed registration code
## can remain unchanged.
if(ncol(info) == 3L)
info <- cbind(info, NA_character_)
Info <- cbind(info[, 1L : 3L, drop = FALSE], methname, info[, 4L])
## <FIXME delayed S3 method registration>
loc <- names(env)
if(any(notex <- match(info[,3], loc, nomatch=0L) == 0L)) { # not %in%
warning(sprintf(ngettext(sum(notex),
"S3 method %s was declared in NAMESPACE but not found",
"S3 methods %s were declared in NAMESPACE but not found"),
paste(sQuote(info[notex, 3]), collapse = ", ")),
call. = FALSE, domain = NA)
Info <- Info[!notex, , drop = FALSE]
}
## <FIXME delayed S3 method registration>
eager <- is.na(Info[, 5L])
delayed <- Info[!eager, , drop = FALSE]
Info <- Info[ eager, , drop = FALSE]
## </FIXME delayed S3 method registration>
## Do local generics first (this could be load-ed if pre-computed).
## However, the local generic could be an S4 takeover of a non-local
## (or local) S3 generic. We can't just pass S4 generics on to
## .registerS3method as that only looks non-locally (for speed).
l2 <- localGeneric <- Info[,1] %in% loc
if(.isMethodsDispatchOn())
for(i in which(localGeneric)) {
genfun <- get(Info[i, 1], envir = env)
if(methods::is(genfun, "genericFunction")) {
localGeneric[i] <- FALSE
registerS3method(Info[i, 1], Info[i, 2], Info[i, 3], env)
}
}
if(any(localGeneric)) {
lin <- Info[localGeneric, , drop = FALSE]
S3MethodsTable <- env[[".__S3MethodsTable__."]]
## we needed to move this to C for speed.
## for(i in seq_len(nrow(lin)))
## assign(lin[i,4], get(lin[i,3], envir = env),
## envir = S3MethodsTable)
.Internal(importIntoEnv(S3MethodsTable, lin[,4], env, lin[,3]))
}
## now the rest
fin <- Info[!l2, , drop = FALSE]
for(i in seq_len(nrow(fin)))
.registerS3method(fin[i, 1], fin[i, 2], fin[i, 3], fin[i, 4], env)
if(package != "MASS" && nrow(overwrite)) {
## MASS is providing methods for stubs in stats.
.fmt <- function(o) {
sprintf(" %s %s",
format(c("method", o[, 1L])),
format(c("from", o[, 2L])))
}
## Unloading does not unregister, so reloading "overwrites":
## hence, always drop same-package overwrites.
overwrite <-
overwrite[overwrite[, 2L] != package, , drop = FALSE]
## (Seen e.g. for recommended packages in reg-tests-3.R.)
if(Sys.getenv("_R_LOAD_CHECK_OVERWRITE_S3_METHODS_") %in% c(package, "all")) {
ind <- overwrite[, 2L] %in%
unlist(tools:::.get_standard_package_names(),
use.names = FALSE)
bad <- overwrite[ind, , drop = FALSE]
if(nr <- nrow(bad)) {
msg <- ngettext(nr,
"Registered S3 method from a standard package overwritten by '%s':",
"Registered S3 methods from standard package(s) overwritten by '%s':",
domain = NA)
msg <- paste(c(sprintf(msg, package), .fmt(bad)),
collapse = "\n")
message(msg, domain = NA)
overwrite <- overwrite[!ind, , drop = FALSE]
}
}
## Do not note when
## * There are no overwrites (left)
## * Env var _R_S3_METHOD_REGISTRATION_NOTE_OVERWRITES_ is set
## to something false (for the time being)
## * Env var _R_CHECK_PACKAGE_NAME_ is set to something
## different than 'package'.
## With the last, when checking we only note overwrites from the
## package under check (as recorded via _R_CHECK_PACKAGE_NAME_).
if((nr <- nrow(overwrite)) &&
is.na(match(tolower(Sys.getenv("_R_S3_METHOD_REGISTRATION_NOTE_OVERWRITES_")),
c("0", "no", "false"))) &&
(!is.na(match(Sys.getenv("_R_CHECK_PACKAGE_NAME_"),
c("", package))))) {
msg <- ngettext(nr,
"Registered S3 method overwritten by '%s':",
"Registered S3 methods overwritten by '%s':",
domain = NA)
msg <- paste(c(sprintf(msg, package), .fmt(overwrite)),
collapse = "\n")
packageStartupMessage(msg, domain = NA)
}
}
## <FIXME delayed S3 method registration>
register_S3_method_delayed <- function(pkg, gen, cls, fun) {
pkg <- pkg # force evaluation
gen <- gen # force evaluation
cls <- cls # force evaluation
fun <- fun # force evaluation
if(isNamespaceLoaded(pkg)) {
registerS3method(gen, cls, fun,
envir = asNamespace(pkg))
}
setHook(packageEvent(pkg, "onLoad"),
function(...) {
registerS3method(gen, cls, fun,
envir = asNamespace(pkg))
})
}
if(nrow(delayed)) {
for(i in seq_len(nrow(delayed))) {
gen <- delayed[i, 1L]
cls <- delayed[i, 2L]
fun <- get(delayed[i, 3L], envir = env)
pkg <- delayed[i, 5L]
register_S3_method_delayed(pkg, gen, cls, fun)
}
}
## </FIXME delayed S3 method registration>
## Provide useful error message to user in case of ncol() mismatch:
nsI <- getNamespaceInfo(env, "S3methods")
if(!is.null(p1 <- ncol(nsI)) && !is.null(p2 <- ncol(info)) && p1 != p2)
stop(gettextf(
paste('While loading namespace "%s": "%s" differ in ncol(.), env=%d, newNS=%d.',
"Maybe package installed with version of R newer than %s ?",
sep="\n"),
package, "S3methods", p1, p2, getRversion()), domain = NA)
setNamespaceInfo(env, "S3methods", rbind(info, nsI))
}
.mergeImportMethods <- function(impenv, expenv, metaname)
{
impMethods <- impenv[[metaname]]
if(!is.null(impMethods))
impenv[[metaname]] <-
methods:::.mergeMethodsTable2(impMethods,
newtable = expenv[[metaname]], # known to exist by caller
expenv, metaname)
impMethods # possibly NULL
}