Dynamically compiling scala class files at run time in scala 2.11

I have the following code that works in Scala 2.10 to compile external classes at runtime in Scala

/** * Compile scala files and keep them loaded in memory * @param classDir Directory storing the generated scala files * @throws IOException if there is problem reading the source files * @return Classloader that contains the compiled external classes */ @throws[IOException] def compileFiles(classDir: String): AbstractFileClassLoader = { val files = recursiveListFiles(new File(classDir)) .filter(_.getName.endsWith("scala")) println("Loaded files: \n" + files.mkString("[", ",\n", "]")) val settings: GenericRunnerSettings = new GenericRunnerSettings(err => println("Interpretor error: " + err)) settings.usejavacp.value = true val interpreter: IMain = new IMain(settings) files.foreach(f => { interpreter.compileSources(new BatchSourceFile(AbstractFile.getFile(f))) }) interpreter.getInterpreterClassLoader() } 

And then in another place I could use the link to the class loader to instantiate the classes, for example.

 val personClass = classLoader.findClass("com.example.dynacsv.PersonData") val ctor = personClass.getDeclaredConstructors()(0) val instance = ctor.newInstance("Mr", "John", "Doe", 25: java.lang.Integer, 165: java.lang.Integer, 1: java.lang.Integer) println("Instantiated class: " + instance.getClass.getCanonicalName) println(instance.toString) 

However, the above does not work, since the getInterpreterClassLoader method getInterpreterClassLoader been removed from scala.tools.nsc.interpreter.IMain . In addition, AbstractFileClassLoader has been moved and is deprecated. It is no longer allowed to call the findClass method in the class loader from the external package.

What is the recommended way to do this in Scala 2.11? Thanks!

+7
scala
source share
1 answer

If you want to run scala outer classes at runtime, I would suggest using eval with scala.tools.reflect.ToolBox (it's included in the REPL, but for normal use you need to add scala -reflect.jar)

 import scala.reflect.runtime.universe import scala.tools.reflect.ToolBox val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox() tb.eval(tb.parse("""println("hello!")""")) 

You can also compile files using tb.compile .

Modified example: suppose you have an external file with

 class PersonData() { val field = 42 } scala.reflect.classTag[PersonData].runtimeClass 

So you do

 val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]] val ctor = clazz.getDeclaredConstructors()(0) val instance = ctor.newInstance() 

Additional features are (almost) unlimited, you can get the full AST tree and work with it however you want:

 showRaw(tb.parse(src)) // this is AST of external file sources // this is quasiquote val q""" class $name { ..$stats } scala.reflect.classTag[PersonData].runtimeClass """ = tb.parse(src) // name: reflect.runtime.universe.TypeName = PersonData // stats: List[reflect.runtime.universe.Tree] = List(val field = 42) println(name) // PersonData 

See the official documentation for these tricks:

http://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html

http://docs.scala-lang.org/overviews/quasiquotes/intro.html

It is worth noting that scala-reflect is being replaced by scala -meta . However, for this example, scala-reflect should be sufficient.

+11
source share

All Articles