diff --git a/compiler/resources/META-INF/services/javax.script.ScriptEngineFactory b/compiler/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 000000000000..2a0ca4d4298d --- /dev/null +++ b/compiler/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +dotty.tools.repl.ScriptEngine$Factory diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 9d130b5166fc..d18bb2b3e6ed 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -26,7 +26,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) { private[this] var myClassLoader: ClassLoader = _ /** Class loader used to load compiled code */ - private[this] def classLoader()(implicit ctx: Context) = + private[repl] def classLoader()(implicit ctx: Context) = if (myClassLoader != null) myClassLoader else { val parent = parentClassLoader.getOrElse { diff --git a/compiler/src/dotty/tools/repl/ScriptEngine.scala b/compiler/src/dotty/tools/repl/ScriptEngine.scala new file mode 100644 index 000000000000..b3e80887f443 --- /dev/null +++ b/compiler/src/dotty/tools/repl/ScriptEngine.scala @@ -0,0 +1,74 @@ +package dotty.tools +package repl + +import java.io.Reader +import javax.script.{AbstractScriptEngine, Bindings, ScriptContext, ScriptEngine => JScriptEngine, ScriptEngineFactory, ScriptException, SimpleBindings} +import dotc.core.StdNames.str + +/** A JSR 223 (Scripting API) compatible wrapper around the REPL for improved + * interoperability with software that supports it. + * + * It works by instantiating a new script engine through the script engine manager. + * The script engine provides a eval method to evaluate scripts in string form. + * Example use: + * + * val m = new javax.script.ScriptEngineManager() + * val e = m.getEngineByName("scala") + * println(e.eval("42")) + */ +class ScriptEngine extends AbstractScriptEngine { + private[this] val driver = new ReplDriver(Array("-usejavacp", "-color:never"), Console.out, None) + private[this] val rendering = new Rendering + private[this] var state: State = driver.initialState + + def getFactory: ScriptEngineFactory = new ScriptEngine.Factory + + def createBindings: Bindings = new SimpleBindings + + /* Evaluate with the given context. */ + @throws[ScriptException] + def eval(script: String, context: ScriptContext): Object = { + val vid = state.valIndex + state = driver.run(script)(state) + val oid = state.objectIndex + Class.forName(s"${str.REPL_SESSION_LINE}$oid", true, rendering.classLoader()(state.context)) + .getDeclaredMethods.find(_.getName == s"${str.REPL_RES_PREFIX}$vid") + .map(_.invoke(null)) + .getOrElse(null) + } + + @throws[ScriptException] + def eval(reader: Reader, context: ScriptContext): Object = throw new UnsupportedOperationException +} + +object ScriptEngine { + import java.util.Arrays + import scala.util.Properties + + class Factory extends ScriptEngineFactory { + def getEngineName = "Scala REPL" + def getEngineVersion = "3.0" + def getExtensions = Arrays.asList("scala") + def getLanguageName = "Scala" + def getLanguageVersion = Properties.versionString + def getMimeTypes = Arrays.asList("application/x-scala") + def getNames = Arrays.asList("scala") + + def getMethodCallSyntax(obj: String, m: String, args: String*) = s"$obj.$m(${args.mkString(", ")})" + + def getOutputStatement(toDisplay: String) = s"""print("$toDisplay")""" + + def getParameter(key: String): Object = key match { + case JScriptEngine.ENGINE => getEngineName + case JScriptEngine.ENGINE_VERSION => getEngineVersion + case JScriptEngine.LANGUAGE => getLanguageName + case JScriptEngine.LANGUAGE_VERSION => getLanguageVersion + case JScriptEngine.NAME => getNames.get(0) + case _ => null + } + + def getProgram(statements: String*) = statements.mkString("; ") + + def getScriptEngine: JScriptEngine = new ScriptEngine + } +} diff --git a/tests/run-with-compiler/scripting.check b/tests/run-with-compiler/scripting.check new file mode 100644 index 000000000000..2585ccd6c17d --- /dev/null +++ b/tests/run-with-compiler/scripting.check @@ -0,0 +1,3 @@ +val res0: Int = 42 + +42 diff --git a/tests/run-with-compiler/scripting.scala b/tests/run-with-compiler/scripting.scala new file mode 100644 index 000000000000..2eb209201a0c --- /dev/null +++ b/tests/run-with-compiler/scripting.scala @@ -0,0 +1,8 @@ +object Test { + def main(args: Array[String]): Unit = { + val m = new javax.script.ScriptEngineManager(getClass().getClassLoader()) + val e = m.getEngineByName("scala") + println(e.eval("42")) + } +} +