I'm trying to use the scala.tools.nsc.interpreter.IMain class within an
OSGi framework, and am stumbling on the following stack trace (using
scala 2.9.1):
Caused by: java.lang.NullPointerException
at scala.tools.nsc.Global$Run.<init>(Global.scala:663)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compileAndSaveRun(IMain.scala:755)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compile(IMain.scala:731)
at scala.tools.nsc.interpreter.IMain.bind(IMain.scala:591)
at scala.tools.nsc.interpreter.IMain.bind(IMain.scala:626)
which is triggered by code like that:
val mapping : Array[String] => (F, L) = {
val code : String = {
val path = "myCode.scala"
var code : String = null
def resource = this.getClass.getClassLoader.getResourceAsStream(_)
val source = scala.io.Source.fromInputStream (resource (path))
source.withClose(() => code = source.mkString); code
}
import scala.tools.nsc._
val settings = new Settings; settings.usejavacp.value = true
val interp = new interpreter.IMain (settings) {
interp =>
def execute (code : String) : Array[String] => (F, L) = {
val result = new Array[Array[String] => (F, L)](1)
/* Blows up on the following line: */
interp.bind(interpreter.NamedParam("res", "Array[Array[String] => (F, L)]", result))
interp.interpret(code)
result(0)
}
}
While this kind of code works almost fine in the REPL, it blows up when
deployed in an OSGi bundle. As it is a constructor of Global.Run that
fails, it looks like I have failed to initialise it properly. Would very
much like to know where the 2.9.1 version of scala.tools.nsc is
documented in detail.
--
Guillaume Yziquel
Crossing-Tech
After digging a bit into the source code of the IMain class, I noticed
that the NullPointerException is triggered because to access the Run
constructor, you need to already have the englobing Global class
instantiated in the IMain.global lazy val.
In my case, IMain.global is evaluated to null, which means that the
following code fails to initialise properly the IMain.global val.
/** the public, go through the future compiler */
lazy val global: Global = {
if (isInitializeComplete) _compiler
else {
// If init hasn't been called yet you're on your own.
if (_isInitialized == null) {
repldbg("Warning: compiler accessed before init set up. Assuming no postInit code.")
initialize(())
}
// blocks until it is ; false means catastrophic failure
if (_isInitialized()) _compiler
else null /* Execution ends up getting there */
}
}
How do I make sure that the IMain.global value is properly initialised?
Le Thursday 16 Feb 2012 � 09:30:05 (+0100), Guillaume Yziquel a �crit :
I do not know exactly. I mean, I knew, and I do not know any more.
Using an embedded compiler, it seems to me that the most mature projects
are Sling's Guggla embedding of the Scala compiler (there's a version
working in Scalate's source code), and Slang for deployment in an OSGi
container.
The embedded interpreter and compiler are indeed quite sensitive to many
small details. It usually works nicely in the REPL, but you have
surprises when trying to run in richer environments. That's why I'd now
avoid trying to reinvent the wheel, and reuse stuff from Guggla or
Slang, depending on what you want (which is the real issue).
--
Guillaume Yziquel.
This is typical of a classloading issue of the default scala jar. I've
experienced it in an OSGi environment.
The main issue is that the classloading inside the scala compiler does
not use classloader, but is essentially filesystem based.
However, the Scala compiler API provides the notion of AbstractFiles,
which allows to "fake" a filesystem on top of OSGi bundles (in my case).
So there are two steps:
[1] Implement such a fake filesystem to help the scala compiler dive
into the OSGi bundles.
[2] Provide the classpath to the compiler. As the compiler expects the
classpath to be fed in a textual format, there is no way to
"off-the-shelf", use the AbstractFile trait.
You have to override the rootClassloader of the Global class.
See here how this is done for on-the-fly compilation and deployment of
OSGi bundles.
(Arguably not the best way to do it.)
Not so sure about compiling classes directly in OSGi environments.
Deployment of compiled bundles seems to me an important step to ensure
safety of the code (class cast exceptions).
Hope this helps if you have more time.
--
Guillaume Yziquel
Crossing-Tech
Parc Scientifique EPFL