diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompiledEvaluator.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompiledEvaluator.kt index 27d56412417..1e675d05a84 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompiledEvaluator.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompiledEvaluator.kt @@ -41,7 +41,13 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable, private val compiledLoadedClassesHistory = ReplHistory() - override fun eval(codeLine: ReplCodeLine, history: List, compiledClasses: List, hasResult: Boolean, classpathAddendum: List): ReplEvalResult /*= evalStateLock.write*/ { + override fun eval(codeLine: ReplCodeLine, + history: List, + compiledClasses: List, + hasResult: Boolean, + classpathAddendum: List, + invokeWrapper: InvokeWrapper? + ): ReplEvalResult /*= evalStateLock.write*/ { checkAndUpdateReplHistoryCollection(compiledLoadedClassesHistory, history)?.let { return@eval ReplEvalResult.HistoryMismatch(compiledLoadedClassesHistory.lines, it) } @@ -85,7 +91,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable, val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams) val scriptInstance = try { - evalWithIO { scriptInstanceConstructor.newInstance(*constructorArgs) } + invokeWrapper?.invoke { scriptInstanceConstructor.newInstance(*constructorArgs) } ?: scriptInstanceConstructor.newInstance(*constructorArgs) } catch (e: Throwable) { // ignore everything in the stack trace until this constructor call @@ -109,16 +115,16 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable, return ReplScriptInvokeResult.ValueResult(klass.safeCast(receiver)) } - override fun invokeMethod(receiver: Any, name: String, vararg args: Any?): ReplScriptInvokeResult = evalStateLock.read { - return invokeImpl(receiver.javaClass.kotlin, receiver, name, args) + override fun invokeMethod(receiver: Any, name: String, vararg args: Any?, invokeWrapper: InvokeWrapper?): ReplScriptInvokeResult = evalStateLock.read { + return invokeImpl(receiver.javaClass.kotlin, receiver, name, args, invokeWrapper) } - override fun invokeFunction(name: String, vararg args: Any?): ReplScriptInvokeResult = evalStateLock.read { + override fun invokeFunction(name: String, vararg args: Any?, invokeWrapper: InvokeWrapper?): ReplScriptInvokeResult = evalStateLock.read { val (klass, instance) = compiledLoadedClassesHistory.values.lastOrNull() ?: return ReplScriptInvokeResult.Error.NoSuchEntity("no script ") - return invokeImpl(klass.kotlin, instance, name, args) + return invokeImpl(klass.kotlin, instance, name, args, invokeWrapper) } - private fun invokeImpl(receiverClass: KClass<*>, receiverInstance: Any, name: String, args: Array): ReplScriptInvokeResult { + private fun invokeImpl(receiverClass: KClass<*>, receiverInstance: Any, name: String, args: Array, invokeWrapper: InvokeWrapper?): ReplScriptInvokeResult { val candidates = receiverClass.memberFunctions.filter { it.name == name } + receiverClass.memberExtensionFunctions.filter { it.name == name } @@ -126,7 +132,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable, candidates.findMapping(listOf(receiverInstance) + args) ?: return ReplScriptInvokeResult.Error.NoSuchEntity("no suitable function '$name' found") val res = try { - evalWithIO { fn.callBy(mapping) } + invokeWrapper?.invoke { fn.callBy(mapping) } ?: fn.callBy(mapping) } catch (e: Throwable) { // ignore everything in the stack trace until this constructor call diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt index 5f3f249396f..e1b0d8d67fc 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt @@ -120,8 +120,8 @@ interface ReplCompiler : ReplChecker { interface ReplScriptInvoker { fun getInterface(clasz: KClass): ReplScriptInvokeResult fun getInterface(receiver: Any, clasz: KClass): ReplScriptInvokeResult - fun invokeMethod(receiver: Any, name: String, vararg args: Any?): ReplScriptInvokeResult - fun invokeFunction(name: String, vararg args: Any?): ReplScriptInvokeResult + fun invokeMethod(receiver: Any, name: String, vararg args: Any?, invokeWrapper: InvokeWrapper? = null): ReplScriptInvokeResult + fun invokeFunction(name: String, vararg args: Any?, invokeWrapper: InvokeWrapper? = null): ReplScriptInvokeResult } // TODO this is a bit cumbersome, consider some other ways to access an invoker @@ -131,17 +131,24 @@ interface ReplScriptInvokerProxy { interface ReplCompiledEvaluator { - fun eval(codeLine: ReplCodeLine, history: List, compiledClasses: List, hasResult: Boolean, classpathAddendum: List): ReplEvalResult - - // override to capture output - fun evalWithIO(body: () -> T): T = body() + fun eval(codeLine: ReplCodeLine, + history: List, + compiledClasses: List, + hasResult: Boolean, + classpathAddendum: List, + invokeWrapper: InvokeWrapper? = null + ): ReplEvalResult } interface ReplEvaluator : ReplChecker { - fun eval(codeLine: ReplCodeLine, history: List): ReplEvalResult + fun eval(codeLine: ReplCodeLine, + history: List, + invokeWrapper: InvokeWrapper? = null + ): ReplEvalResult +} - // override to capture output - fun evalWithIO(body: () -> T): T = body() +interface InvokeWrapper { + operator fun invoke(body: () -> T): T // e.g. for capturing io } \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt index b0fc01e4de3..c0f9f0a9164 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericRepl.kt @@ -191,18 +191,18 @@ open class GenericRepl( override val scriptInvoker: ReplScriptInvoker get() = compiledEvaluator @Synchronized - override fun eval(codeLine: ReplCodeLine, history: List): ReplEvalResult = - compileAndEval(this, compiledEvaluator, codeLine, history) + override fun eval(codeLine: ReplCodeLine, history: List, invokeWrapper: InvokeWrapper?): ReplEvalResult = + compileAndEval(this, compiledEvaluator, codeLine, history, invokeWrapper) } -fun compileAndEval(replCompiler: ReplCompiler, replCompiledEvaluator: ReplCompiledEvaluator, codeLine: ReplCodeLine, history: List): ReplEvalResult = +fun compileAndEval(replCompiler: ReplCompiler, replCompiledEvaluator: ReplCompiledEvaluator, codeLine: ReplCodeLine, history: List, invokeWrapper: InvokeWrapper?): ReplEvalResult = replCompiler.compile(codeLine, history).let { when (it) { is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete(it.updatedHistory) is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(it.updatedHistory, it.lineNo) is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(it.updatedHistory, it.message, it.location) - is ReplCompileResult.CompiledClasses -> replCompiledEvaluator.eval(codeLine, history, it.classes, it.hasResult, it.classpathAddendum) + is ReplCompileResult.CompiledClasses -> replCompiledEvaluator.eval(codeLine, history, it.classes, it.hasResult, it.classpathAddendum, invokeWrapper) } } diff --git a/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinRemoteReplClient.kt b/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinRemoteReplClient.kt index 31c535ab61b..b99e7eef293 100644 --- a/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinRemoteReplClient.kt +++ b/compiler/daemon/daemon-client/src/org/jetbrains/kotlin/daemon/client/KotlinRemoteReplClient.kt @@ -138,7 +138,8 @@ class KotlinRemoteReplEvaluator( operationsTracer = operationsTracer ), ReplEvaluator { - override fun eval(codeLine: ReplCodeLine, history: List): ReplEvalResult { + // TODO: invokeWrapper is ignored here, and in the daemon the session wrapper is used instead; So consider to make it per call (avoid performance penalties though) + override fun eval(codeLine: ReplCodeLine, history: List, invokeWrapper: InvokeWrapper?): ReplEvalResult { return compileService.remoteReplLineEval(sessionId, codeLine, history).get() } } diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt index bb66a891069..3ea1d8847b6 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt @@ -115,32 +115,32 @@ open class KotlinJvmReplService( else GenericReplCompiler(disposable, scriptDef, configuration, messageCollector) } - private val compiledEvaluator : GenericReplCompiledEvaluator by lazy { - if (evalOutputStream == null && evalErrorStream == null && evalInputStream == null) - GenericReplCompiledEvaluator(configuration.jvmClasspathRoots, null, scriptArgs, scriptArgsTypes) - else object : GenericReplCompiledEvaluator(configuration.jvmClasspathRoots, null, scriptArgs, scriptArgsTypes) { - val out = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(evalOutputStream!!, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE)) - val err = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(evalErrorStream!!, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE)) - val `in` = BufferedInputStream(RemoteInputStreamClient(evalInputStream!!, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE) - override fun evalWithIO(body: () -> T): T { - val prevOut = System.out - System.setOut(out) - val prevErr = System.err - System.setErr(err) - val prevIn = System.`in` - System.setIn(`in`) + private val invokeWrapper : InvokeWrapper? by lazy { + if (evalOutputStream == null && evalErrorStream == null && evalInputStream == null) null + else object : InvokeWrapper { + val out = evalOutputStream?.let { PrintStream(BufferedOutputStream(RemoteOutputStreamClient(it, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE)) } + val err = evalErrorStream?.let { PrintStream(BufferedOutputStream(RemoteOutputStreamClient(it, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE)) } + val `in` = evalInputStream?.let { BufferedInputStream(RemoteInputStreamClient(it, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE) } + override operator fun invoke(body: () -> T): T { + val prevOut = swapOrNull(out, { System.out }, { System.setOut(it) }) + val prevErr = swapOrNull(err, { System.err }, { System.setErr(it) }) + val prevIn = swapOrNull(`in`, { System.`in` }, { System.setIn(it) }) try { return body() } finally { - System.setIn(prevIn) - System.setErr(prevErr) - System.setOut(prevOut) + prevIn?.let { System.setIn(prevIn) } + prevErr?.let { System.setErr(prevErr) } + prevOut?.let { System.setOut(prevOut) } } } } } + private val compiledEvaluator : GenericReplCompiledEvaluator by lazy { + GenericReplCompiledEvaluator(configuration.jvmClasspathRoots, null, scriptArgs, scriptArgsTypes) + } + override fun check(codeLine: ReplCodeLine, history: List): ReplCheckResult { operationsTracer?.before("check") try { @@ -167,10 +167,10 @@ open class KotlinJvmReplService( } } - override fun eval(codeLine: ReplCodeLine, history: List): ReplEvalResult = synchronized(this) { + override fun eval(codeLine: ReplCodeLine, history: List, invokeWrapper: InvokeWrapper?): ReplEvalResult = synchronized(this) { operationsTracer?.before("eval") try { - return replCompiler?.let { compileAndEval(it, compiledEvaluator, codeLine, history) } + return replCompiler?.let { compileAndEval(it, compiledEvaluator, codeLine, history, invokeWrapper) } ?: ReplEvalResult.Error.CompileTime(history, messageCollector.firstErrorMessage ?: "Unknown error", messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION) @@ -180,3 +180,10 @@ open class KotlinJvmReplService( } } } + +private inline fun swapOrNull(value: T?, get: () -> T, set: (T) -> Unit): T? = + value?.let { + val prevValue = get() + set(value) + prevValue + } \ No newline at end of file