diff options
author | Raphael Jolly <rjolly@users.sourceforge.net> | 2013-03-09 22:02:02 +0100 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2013-03-11 22:36:17 -0700 |
commit | 3a30af154e62f5b8a569486b09788c42db2103a6 (patch) | |
tree | 4f43fbca95a1f9d59e7307826497b36039328006 /src/repl/scala/tools/nsc/interpreter/IMain.scala | |
parent | 3e8f8ddb2ac057ad82e1a7e37f59baff1c0e15ac (diff) | |
download | scala-3a30af154e62f5b8a569486b09788c42db2103a6.tar.gz scala-3a30af154e62f5b8a569486b09788c42db2103a6.tar.bz2 scala-3a30af154e62f5b8a569486b09788c42db2103a6.zip |
SI-874 actual JSR-223 implementation
Diffstat (limited to 'src/repl/scala/tools/nsc/interpreter/IMain.scala')
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/IMain.scala | 174 |
1 files changed, 149 insertions, 25 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index cb2e3c32b8..c92777c13e 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -19,6 +19,11 @@ import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings } import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap import scala.collection.{ mutable, immutable } +import scala.reflect.BeanProperty +import scala.util.Properties.versionString +import javax.script.{AbstractScriptEngine, Bindings, ScriptContext, ScriptEngine, ScriptEngineFactory, ScriptException, SimpleBindings, CompiledScript, Compilable} +import java.io.{ StringWriter, Reader } +import java.util.Arrays import IMain._ import java.util.concurrent.Future import scala.reflect.runtime.{ universe => ru } @@ -57,7 +62,7 @@ import StdReplTags._ * @author Moez A. Abdel-Gawad * @author Lex Spoon */ -class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Imports { +class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Settings, protected val out: JPrintWriter) extends AbstractScriptEngine(new SimpleBindings) with Compilable with Imports { imain => object replOutput extends ReplOutput(settings.Yreploutdir) { } @@ -100,7 +105,10 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } /** construct an interpreter that reports to Console */ + def this(settings: Settings, out: JPrintWriter) = this(null, settings, out) + def this(factory: ScriptEngineFactory, settings: Settings) = this(factory, settings, new NewLinePrintWriter(new ConsoleWriter, true)) def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) + def this(factory: ScriptEngineFactory) = this(factory, new Settings()) def this() = this(new Settings()) lazy val formatting: Formatting = new Formatting { @@ -146,19 +154,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } def isInitializeComplete = _initializeComplete - /** 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.get()) _compiler - else null - } + if (!isInitializeComplete) _initialize() + _compiler } import global._ @@ -535,9 +533,82 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends */ def interpret(line: String): IR.Result = interpret(line, synthetic = false) def interpretSynthetic(line: String): IR.Result = interpret(line, synthetic = true) - def interpret(line: String, synthetic: Boolean): IR.Result = { - def loadAndRunReq(req: Request) = { - classLoader.setAsContext() + def interpret(line: String, synthetic: Boolean): IR.Result = compile(line, synthetic) match { + case Left(result) => result + case Right(req) => new WrappedRequest(req).loadAndRunReq + } + + private def compile(line: String, synthetic: Boolean): Either[IR.Result, Request] = { + if (global == null) Left(IR.Error) + else requestFromLine(line, synthetic) match { + case Left(result) => Left(result) + case Right(req) => + // null indicates a disallowed statement type; otherwise compile and + // fail if false (implying e.g. a type error) + if (req == null || !req.compile) Left(IR.Error) else Right(req) + } + } + + var code = "" + var bound = false + @throws(classOf[ScriptException]) + def compile(script: String): CompiledScript = { + if (!bound) { + quietBind("bindings", getBindings(ScriptContext.ENGINE_SCOPE)) + bound = true + } + val cat = code + script + compile(cat, false) match { + case Left(result) => result match { + case IR.Incomplete => { + code = cat + "\n" + new CompiledScript { + def eval(context: ScriptContext): Object = null + def getEngine: ScriptEngine = IMain.this + } + } + case _ => { + code = "" + throw new ScriptException("compile-time error") + } + } + case Right(req) => { + code = "" + new WrappedRequest(req) + } + } + } + + @throws(classOf[ScriptException]) + def compile(reader: Reader): CompiledScript = { + val writer = new StringWriter() + var c = reader.read() + while(c != -1) { + writer.write(c) + c = reader.read() + } + reader.close() + compile(writer.toString()) + } + + private class WrappedRequest(val req: Request) extends CompiledScript { + var recorded = false + + @throws(classOf[ScriptException]) + def eval(context: ScriptContext): Object = { + val result = req.lineRep.evalEither match { + case Left(e: Exception) => throw new ScriptException(e) + case Left(_) => throw new ScriptException("run-time error") + case Right(result) => result.asInstanceOf[Object] + } + if (!recorded) { + recordRequest(req) + recorded = true + } + result + } + + def loadAndRunReq = classLoader.asContext { val (result, succeeded) = req.loadAndRun /** To our displeasure, ConsoleReporter offers only printMessage, @@ -562,15 +633,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } } - if (global == null) IR.Error - else requestFromLine(line, synthetic) match { - case Left(result) => result - case Right(req) => - // null indicates a disallowed statement type; otherwise compile and - // fail if false (implying e.g. a type error) - if (req == null || !req.compile) IR.Error - else loadAndRunReq(req) - } + def getEngine: ScriptEngine = IMain.this } /** Bind a specified name to a specified value. The name may @@ -703,6 +766,14 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends lazy val evalClass = load(evalPath) + def evalEither = callEither(resultName) match { + case Left(ex) => ex match { + case ex: NullPointerException => Right(null) + case ex => Left(unwrap(ex)) + } + case Right(result) => Right(result) + } + def compile(source: String): Boolean = compileAndSaveRun("<console>", source) /** The innermost object inside the wrapper, found by @@ -738,6 +809,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends mostRecentWarnings = warnings } private def evalMethod(name: String) = evalClass.getMethods filter (_.getName == name) match { + case Array() => null case Array(method) => method case xs => sys.error("Internal error: eval object " + evalClass + ", " + xs.mkString("\n", "\n", "")) } @@ -897,6 +969,16 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends override def toString = "Request(line=%s, %s trees)".format(line, trees.size) } + def createBindings: Bindings = new SimpleBindings + + @throws(classOf[ScriptException]) + def eval(script: String, context: ScriptContext): Object = compile(script).eval(context) + + @throws(classOf[ScriptException]) + def eval(reader: Reader, context: ScriptContext): Object = compile(reader).eval(context) + + override def finalize = close + /** Returns the name of the most recent interpreter result. * Mostly this exists so you can conveniently invoke methods on * the previous result. @@ -1068,6 +1150,48 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** Utility methods for the Interpreter. */ object IMain { + class Factory extends ScriptEngineFactory { + @BeanProperty + val engineName = "Scala Interpreter" + + @BeanProperty + val engineVersion = "1.0" + + @BeanProperty + val extensions: JList[String] = Arrays.asList("scala") + + @BeanProperty + val languageName = "Scala" + + @BeanProperty + val languageVersion = versionString + + def getMethodCallSyntax(obj: String, m: String, args: String*): String = null + + @BeanProperty + val mimeTypes: JList[String] = Arrays.asList("application/x-scala") + + @BeanProperty + val names: JList[String] = Arrays.asList("scala") + + def getOutputStatement(toDisplay: String): String = null + + def getParameter(key: String): Object = key match { + case ScriptEngine.ENGINE => engineName + case ScriptEngine.ENGINE_VERSION => engineVersion + case ScriptEngine.LANGUAGE => languageName + case ScriptEngine.LANGUAGE_VERSION => languageVersion + case ScriptEngine.NAME => names.get(0) + case _ => null + } + + def getProgram(statements: String*): String = null + + def getScriptEngine: ScriptEngine = new IMain(this, new Settings() { + usemanifestcp.value = true + }) + } + // The two name forms this is catching are the two sides of this assignment: // // $line3.$read.$iw.$iw.Bippy = |