使 Scala 解释器在解释调用之间具有遗忘性。

4
有没有可能配置一个Scala解释器(tools.nsc.IMain),使得每次运行下一个interpret()调用时,它都会“忘记”先前执行的代码?
通常情况下,当它编译源代码时,它会将它们包装在嵌套对象中,因此所有先前定义的变量、函数和绑定都可用。
只需不生成嵌套对象(或将其丢弃)即可满足要求,尽管我更喜欢一种解决方案,即甚至可以从类加载器中删除先前编译的类。
是否有设置、方法、可覆盖的内容或替代IMain的东西可以实现这一点?我仍然需要能够从主机VM访问结果对象/类。
基本上,我想隔离后续的interpret()调用,而不必像为每个迭代创建新的IMain那样费力。
1个回答

2

以下是可能的答案。基本上有一个名为reset() 的方法,它会调用以下内容(大多数都是私有的,所以要么你购买整个包裹,要么就不购买):

clearExecutionWrapper()
resetClassLoader()
resetAllCreators()
prevRequests.clear()
referencedNameMap.clear()
definedNameMap.clear()
virtualDirectory.clear()

在我的情况下,我使用了一个定制执行包装器,因此需要再次设置它,并且导入是通过常规解释周期处理的,所以要么再次添加它们,要么更好地只需在执行包装器前置它们。
我想保留我的绑定,但它们也消失了。
import tools.nsc._
import interpreter.IMain

object Test {
  private final class Intp(cset: nsc.Settings)
    extends IMain(cset, new NewLinePrintWriter(new ConsoleWriter, autoFlush = true)) {

    override protected def parentClassLoader = Test.getClass.getClassLoader
  }

  object Foo {
    def bar() { println("BAR" )}
  }

  def run() {
    val cset = new nsc.Settings()
    cset.classpath.value += java.io.File.pathSeparator + sys.props("java.class.path")
    val i = new Intp(cset)
    i.initializeSynchronous()
    i.bind[Foo.type]("foo", Foo)
    val res0 = i.interpret("foo.bar(); val x = 33")
    println(s"res0: $res0")
    i.reset()
    val res1 = i.interpret("println(x)")
    println(s"res1: $res1")
    i.reset()
    val res2 = i.interpret("foo.bar()")
    println(s"res2: $res2")
  }
}

这会在第一次迭代中找到Foo,在第二次迭代中正确地忘记x,但是在第三次迭代中可以看到foo绑定也丢失了:

foo: Test.Foo.type = Test$Foo$@8bf223
BAR
x: Int = 33
res0: Success
<console>:8: error: not found: value x
              println(x)
                      ^
res1: Error
<console>:8: error: not found: value foo
              foo.bar()
              ^
res2: Error

以下内容看起来没问题:
for(j <- 0 until 3) {
  val user  = "foo.bar()"
  val synth =  """import Test.{Foo => foo}
               """.stripMargin + user
  val res = i.interpret(synth)
  println(s"res$j: $res")
  i.reset()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接