Scala REPL аварийно завершает работу при запуске с использованием scala.tools.nsc.interpreter

Я пытаюсь использовать scala.tools.nsc.interpreter для включения интерактивной отладки (например, Python pdb/ipdb):

val foo = 123

import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.{ ILoop, SimpleReader }

val repl = new ILoop
repl.settings = new Settings
repl.settings.usejavacp.value = true
repl.in = SimpleReader()
repl.createInterpreter()

repl.intp.bind("foo", "Int", foo)

repl.loop()
repl.closeInterpreter()

При запуске получаю следующее:

$ scala repl.scala
foo: Int = 123

scala> "hello"   
java.lang.NullPointerException
    at scala.concurrent.Await$$anonfun$ready$1.apply(package.scala:95)
    at scala.concurrent.Await$$anonfun$ready$1.apply(package.scala:95)
    at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53)
    at scala.concurrent.Await$.ready(package.scala:95)
    at scala.tools.nsc.interpreter.ILoop.processLine(ILoop.scala:402)
    at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:430)
    at Main$$anon$1.<init>(repl.scala:14)
    at Main$.main(repl.scala:1)
    at Main.main(repl.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at scala.reflect.internal.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:70)
    at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:101)
    at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:70)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
    at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.ScriptRunner.scala$tools$nsc$ScriptRunner$$runCompiled(ScriptRunner.scala:175)
    at scala.tools.nsc.ScriptRunner$$anonfun$runScript$1.apply(ScriptRunner.scala:192)
    at scala.tools.nsc.ScriptRunner$$anonfun$runScript$1.apply(ScriptRunner.scala:192)
    at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1$$anonfun$apply$mcZ$sp$1.apply(ScriptRunner.scala:161)
    at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply$mcZ$sp(ScriptRunner.scala:161)
    at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply(ScriptRunner.scala:129)
    at scala.tools.nsc.ScriptRunner$$anonfun$withCompiledScript$1.apply(ScriptRunner.scala:129)
    at scala.tools.nsc.util.package$.trackingThreads(package.scala:43)
    at scala.tools.nsc.util.package$.waitingForThreads(package.scala:27)
    at scala.tools.nsc.ScriptRunner.withCompiledScript(ScriptRunner.scala:128)
    at scala.tools.nsc.ScriptRunner.runScript(ScriptRunner.scala:192)
    at scala.tools.nsc.ScriptRunner.runScriptAndCatch(ScriptRunner.scala:205)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:67)
    at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)


Abandoning crashed session.

scala> 

Если я удалю часть repl.intp.bind("foo", "Int", foo), вот что я получу:

$ scala repl.scala 

scala> 123
null

Abandoning crashed session.

scala> 

Что я делаю неправильно? Есть ли более простой способ перейти в интерактивный REPL во время выполнения программы для целей отладки? точки останова, пошаговое выполнение и проверка местных жителей просто иногда не делают этого.

Я на Скала 2.11.5.


person Erik Kaplun    schedule 20.01.2015    source источник
comment
Вы можете попробовать Scalive: github.com/xitrum-framework/scalive   -  person Gábor Bakos    schedule 20.01.2015
comment
@GáborBakos: большое спасибо; кажется потенциально очень полезным!   -  person Erik Kaplun    schedule 22.01.2015


Ответы (1)


Я не знаю, почему в этом комментарии нет волшебных звездочек:

  // start an interpreter with the given settings
  def process(settings: Settings): Boolean

Так что вы можете:

scala> repl process s
Welcome to Scala version 2.11.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :quit
res0: Boolean = true

Или вы могли бы:

package myrepl

import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.{ ILoop, SimpleReader }

object Test extends App {
  val foo = 42
  val repl = new ILoop {
    override def printWelcome() = {
      intp.bind("foo", foo)
      super.printWelcome()
      echo("Customized...")
    }
  }
  val s = new Settings
  s.usejavacp.value = true
  repl.in = SimpleReader()
  repl process s
  /*
  repl.createInterpreter()

  repl.intp.bind("foo", "Int", foo)

  repl.loop()
  repl.closeInterpreter()
  */
}

И

$ scalac myrepl.scala && scala myrepl.Test
foo: Int = 42
Welcome to Scala version 2.11.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
Customized...

scala> foo
res0: Int = 42

scala> :quit

Также,

  s.Xnojline.value  = true
  //repl.in = SimpleReader()

Я собираюсь перейти на 2.11.4 из-за ошибки сбоя repl...

Sbt делает это путем переопределения createInterpreter и вызывая там привязку.

Редактировать:

  val repl = new ILoop {
    override def printWelcome() = {
      //import scala.concurrent.duration._
      //Await.ready(globalFuture, 10.minutes) // sorry, it's private!
      super.printWelcome()
      echo("Customizing...")
      processLine("") // block for init to finish
      intp.bind("foo", foo)
    }
  }

Частный globalFuture является препятствием для взрыва стартапа:

object Solid extends App {
  val foo = 42
  val repl = new ILoop {
    override def printWelcome() = {
      super.printWelcome()
      echo("Customized...")
    }
  }
  val s = new Settings
  s.Xnojline.value = true
  s.usejavacp.value = true
  repl.settings = s
  repl.createInterpreter()
  repl.in = SimpleReader()
  repl.intp.initializeSynchronous()
  repl.loopPostInit()
  repl.globalFuture = concurrent.Future.successful(true)

  repl.intp.bind("foo", "Int", foo)

  try repl.loop()
  finally repl.closeInterpreter()
}

Опция -Yrepl-sync больше не поддерживается в версии 2.11.

person som-snytt    schedule 21.01.2015
comment
Благодарность! если я заменю вызов .loop() на .process(repl.settings), он будет работать как заявлено, однако оценка foo даст error: not found: value foo — можно ли привязать имена к REPL? - person Erik Kaplun; 21.01.2015
comment
@ErikAllik Запуск необходимо переделать, также посмотрите, как sbt запускает свою консоль. Также есть -i для инициализации разного. - person som-snytt; 21.01.2015
comment
Запуск вашего кода без изменений на 2.11.4 или 2.11.5 дает мне: scala.reflect.internal.Symbols$CyclicReference: illegal cyclic reference involving object Predef, а затем, если я продолжаю повторять попытку, он недетерминированно либо работает, либо печатает ту же ошибку, но не падает... - person Erik Kaplun; 22.01.2015
comment
@ErikAllik, вызывающий processLine в printWelcome, - мой лучший способ дождаться завершения инициализации компилятора. См. редактирование. ХТН. - person som-snytt; 22.01.2015