设置parent.env,然后是`detach`,segfaults

时间:2014-04-01 15:36:45

标签: r environment

以下代码在R(3.0.2中执行但是我假设它与其他版本类似)时可重复地出现段错误:

ns = new.env(parent = .BaseNamespaceEnv)
local(f <- function () 42, envir = ns)
x = list2env(as.list(ns), parent = as.environment(2))
parent.env(.GlobalEnv) = x
detach()

是的,我知道documentation on parent.env

  

替换函数parent.env<-非常危险,因为它可以用于以违反内部C代码假设的方式破坏性地更改环境。它可能会在不久的将来被删除。

这就是我在这里遇到的情况。但是,我想了解为什么这种行为就是这样,以及如何避免它。

以下简化代码会出现此问题:

x = new.env(parent = as.environment(2))
local(f <- function () 42, envir = x)
parent.env(.GlobalEnv) = x
detach()

...因此,x包含parent.env不同的(未附加)环境的函数似乎有所不同。

同样,使用attach代替parent.env<-不会导致崩溃。 (那么为什么不简单地使用attach?因为在我的代码中,.GlobalEnv部分是一个可能引用不同环境的变量。)

崩溃转储告诉我,段错误发生在do_detachenvir.c)。该代码包含以下行:

isSpecial = IS_USER_DATABASE(s);
if(isSpecial) {
    R_ObjectTable *tb = (R_ObjectTable*) R_ExternalPtrAddr(HASHTAB(s));
    if(tb->onDetach) tb->onDetach(tb);
}

我不知道IS_USER_DATABASE做了什么 - 也许这是相关的?仅仅在我的环境中添加.onDetach方法(.onDetach = function (x) x)并没有帮助。

2 个答案:

答案 0 :(得分:1)

注意:这不是一个评论而是一个真实的答案,但不能在评论字段中使用它。我有一个 loosely related question ,一直在努力更好地理解parent.env<-的限制。

在您的情况下,请注意问题实际上与list2env有关,而不是函数。考虑:

f.1 <- function() NULL
ns.1 <- new.env(parent = .BaseNamespaceEnv)
x.1 <- new.env(parent = as.environment(2))
environment(f.1) <- ns.1
x.1$f.1 <- f.1
parent.env(.GlobalEnv) <- x.1
detach()

工作,但是:

x.2 <- list2env(list(a=1), parent=as.environment(2))
parent.env(.GlobalEnv) <- x.2
detach()

崩溃。在这里,除了用list2env创建的环境之外,我们甚至没有对我们作为父级使用的环境做任何复杂的事情。查看来源我不明白为什么list2env是一个问题,但$<-不是因为内部似乎都使用defineVar,但显然有很多去那里,我不明白。

答案 1 :(得分:0)

为了完整起见,明显的解决方法是测试环境是否为.GlobalEnv,以及特殊情况:

new_env = something # e.g. .GlobalEnv

if (identical(new_env, .GlobalEnv))
    attach(x)
else {
    parent.env(x) = parent.env(new_env)
    parent.env(new_env) = x
}

...但这并不是解释最初的错误,我担心,只会隐藏问题而不是删除它。