diff --git a/compiler/cli/cli-common/cli-common.iml b/compiler/cli/cli-common/cli-common.iml index 1cf8d3edbb4..7447a45530f 100644 --- a/compiler/cli/cli-common/cli-common.iml +++ b/compiler/cli/cli-common/cli-common.iml @@ -11,5 +11,6 @@ + \ No newline at end of file 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 new file mode 100644 index 00000000000..07740419453 --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/GenericReplCompiledEvaluator.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.common.repl + +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import java.io.File +import java.net.URLClassLoader +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write + +open class GenericReplCompiledEvaluator(baseClasspath: Iterable) : ReplCompiledEvaluator { + + private val classpath = baseClasspath.toMutableList() + + private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = + org.jetbrains.kotlin.cli.common.repl.ReplClassLoader(URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), Thread.currentThread().contextClassLoader)) + private val classLoaderLock = ReentrantReadWriteLock() + + private class ClassWithInstance(val klass: Class<*>, val instance: Any) + + private val compiledLoadedClassesHistory = arrayListOf>() + + override fun eval(codeLine: ReplCodeLine, history: Iterable, compiledClasses: List, hasResult: Boolean): ReplEvalResult { + + checkAndUpdateReplHistoryCollection(compiledLoadedClassesHistory, history)?.let { + return@eval ReplEvalResult.HistoryMismatch(it) + } + + classLoaderLock.read { + compiledClasses.filter { it.path.endsWith(".class") } + .forEach { + classLoader.addClass(JvmClassName.byInternalName(it.path.replaceFirst("\\.class$".toRegex(), "")), it.bytes) + } + } + + val scriptClass = classLoaderLock.read { classLoader.loadClass("Line${codeLine.no}") } + + val constructorParams = compiledLoadedClassesHistory.map { it.second.klass }.toTypedArray() + val constructorArgs = compiledLoadedClassesHistory.map { it.second.instance }.toTypedArray() + + val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams) + val scriptInstance = + try { + evalWithIO { scriptInstanceConstructor.newInstance(*constructorArgs) } + } + catch (e: Throwable) { + // ignore everything in the stack trace until this constructor call + return ReplEvalResult.Error.Runtime(renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.")) + } + + compiledLoadedClassesHistory.add(codeLine to ClassWithInstance(scriptClass, scriptInstance)) + + val rvField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true } + val rv: Any? = rvField.get(scriptInstance) + + return if (hasResult) ReplEvalResult.ValueResult(rv) else ReplEvalResult.UnitResult + } + + fun dependenciesAdded(newClasspath: List) { + classLoaderLock.write { + classpath.addAll(newClasspath) + classLoader = org.jetbrains.kotlin.cli.common.repl.ReplClassLoader(URLClassLoader(newClasspath.map { it.toURI().toURL() }.toTypedArray(), classLoader)) + } + } + + companion object { + private val SCRIPT_RESULT_FIELD_NAME = "\$\$result" + } +} \ No newline at end of file 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 new file mode 100644 index 00000000000..c6d18ee5e9c --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/Repl.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.common.repl + +import java.io.File +import java.io.Serializable +import java.util.* + +data class ReplCodeLine(val no: Int, val code: String) + +data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializable { + override fun equals(other: Any?): Boolean = (other as? CompiledClassData)?.let { path == it.path && Arrays.equals(bytes, it.bytes) } ?: false + override fun hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes) +} + +sealed class ReplCheckResult : Serializable { + object Ok : ReplCheckResult() + object Incomplete : ReplCheckResult() + class Error(val message: String) : ReplCheckResult() +} + +sealed class ReplCompileResult : Serializable { + class CompiledClasses(val classes: List, val hasResult: Boolean) : ReplCompileResult() + object Incomplete : ReplCompileResult() + class HistoryMismatch(val lineNo: Int): ReplCompileResult() + class Error(val message: String) : ReplCompileResult() +} + +sealed class ReplEvalResult : Serializable { + class ValueResult(val value: Any?) : ReplEvalResult() + object UnitResult : ReplEvalResult() + object Incomplete : ReplEvalResult() + class HistoryMismatch(val lineNo: Int): ReplEvalResult() + sealed class Error(val message: String) : ReplEvalResult() { + class Runtime(message: String) : Error(message) + class CompileTime(message: String) : Error(message) + } +} + +interface ReplChecker { + fun check(codeLine: ReplCodeLine, history: Iterable): ReplCheckResult +} + +interface ReplCompiler : ReplChecker { + fun compile(codeLine: ReplCodeLine, history: Iterable): ReplCompileResult + + // a callback + fun dependenciesAdded(classpath: List) +} + +interface ReplCompiledEvaluator { + + fun eval(codeLine: ReplCodeLine, history: Iterable, compiledClasses: List, hasResult: Boolean): ReplEvalResult + + // override to capture output + fun evalWithIO(body: () -> T): T = body() +} + + +interface ReplEvaluator : ReplChecker { + + fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult + + fun dependenciesAdded(classpath: List) + + // override to capture output + fun evalWithIO(body: () -> T): T = body() +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplClassLoader.java b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplClassLoader.java similarity index 95% rename from compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplClassLoader.java rename to compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplClassLoader.java index c1d689043b1..e730b48cfde 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplClassLoader.java +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/ReplClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.cli.jvm.repl; +package org.jetbrains.kotlin.cli.common.repl; import com.google.common.collect.Maps; import org.jetbrains.annotations.NotNull; diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt new file mode 100644 index 00000000000..1f8e91ed5c8 --- /dev/null +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/repl/replUtil.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.common.repl + +import com.google.common.base.Throwables + +fun checkAndUpdateReplHistoryCollection(col: MutableList>, baseHistory: Iterable): Int? { + val baseHistoryIt = baseHistory.iterator() + var idx = 0 + while (baseHistoryIt.hasNext()) { + val curLine = baseHistoryIt.next() + if (col[idx].first != curLine) return curLine.no + idx += 1 + } + col.dropLast(col.size - idx) + return null +} + +fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String { + val newTrace = arrayListOf() + var skip = true + for ((i, element) in cause.stackTrace.withIndex().reversed()) { + if ("${element.className}.${element.methodName}" == startFromMethodName) { + skip = false + } + if (!skip) { + newTrace.add(element) + } + } + + val resultingTrace = newTrace.reversed().dropLast(1) + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UsePropertyAccessSyntax") + (cause as java.lang.Throwable).setStackTrace(resultingTrace.toTypedArray()) + + return Throwables.getStackTraceAsString(cause) +} + 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 4e5f97c65b4..518f574f562 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 @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.cli.jvm.repl -import com.google.common.base.Throwables import com.intellij.openapi.Disposable import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.CharsetToolkit @@ -26,6 +25,7 @@ import com.intellij.testFramework.LightVirtualFile import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.repl.* import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot @@ -41,100 +41,97 @@ import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies import org.jetbrains.kotlin.script.ScriptParameter -import java.net.URLClassLoader -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read -import kotlin.concurrent.write +import java.io.File private val logger = Logger.getInstance(GenericRepl::class.java) -open class GenericRepl( +open class GenericReplChecker( disposable: Disposable, val scriptDefinition: KotlinScriptDefinition, val compilerConfiguration: CompilerConfiguration, messageCollector: MessageCollector -) { - private val environment = run { +) : ReplChecker { + protected val environment = run { compilerConfiguration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, DelegatingScriptDefWithNoParams(scriptDefinition)) compilerConfiguration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) KotlinCoreEnvironment.createForProduction(disposable, compilerConfiguration, EnvironmentConfigFiles.JVM_CONFIG_FILES) } - private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl - private val analyzerEngine = CliReplAnalyzerEngine(environment) + protected val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl // "line" - is the unit of evaluation here, could in fact consists of several character lines - private class LineState( - val code: String, + protected class LineState( + val codeLine: ReplCodeLine, val psiFile: KtFile, val errorHolder: DiagnosticMessageHolder) - sealed class EvalResult { - class ValueResult(val value: Any?): EvalResult() - - object UnitResult: EvalResult() - object Ready: EvalResult() - object Incomplete : EvalResult() - - sealed class Error(val errorText: String): EvalResult() { - class Runtime(errorText: String): Error(errorText) - class CompileTime(errorText: String): Error(errorText) - } - } - - private class DelegatingScriptDefWithNoParams(parent: KotlinScriptDefinition) : KotlinScriptDefinition by parent { + protected class DelegatingScriptDefWithNoParams(parent: KotlinScriptDefinition) : KotlinScriptDefinition by parent { override fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List = emptyList() } - private var lineState: LineState? = null + protected var lineState: LineState? = null - private var lastDependencies: KotlinScriptExternalDependencies? = null - - val classpath = compilerConfiguration.jvmClasspathRoots.toMutableList() - - private var classLoader: ReplClassLoader = - ReplClassLoader(URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), Thread.currentThread().contextClassLoader)) - private val classLoaderLock = ReentrantReadWriteLock() - - private val earlierLines = arrayListOf() + val classpath: MutableList = compilerConfiguration.jvmClasspathRoots.toMutableList() fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder() - fun checkComplete(executionNumber: Long, code: String): EvalResult { + override fun check(codeLine: ReplCodeLine, history: Iterable): ReplCheckResult { synchronized(this) { val virtualFile = - LightVirtualFile("line$executionNumber${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, code).apply { + LightVirtualFile("line${codeLine.no}${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply { charset = CharsetToolkit.UTF8_CHARSET } val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile? - ?: error("Script file not analyzed at line $executionNumber: $code") + ?: error("Script file not analyzed at line ${codeLine.no}: ${codeLine.code}") val errorHolder = createDiagnosticHolder() val syntaxErrorReport = AnalyzerWithCompilerReport.Companion.reportSyntaxErrors(psiFile, errorHolder) if (!syntaxErrorReport.isHasErrors) { - lineState = LineState(code, psiFile, errorHolder) + lineState = LineState(codeLine, psiFile, errorHolder) } return when { - syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> EvalResult.Incomplete - syntaxErrorReport.isHasErrors -> EvalResult.Error.CompileTime(errorHolder.renderedDiagnostics) - else -> EvalResult.Ready + syntaxErrorReport.isHasErrors && syntaxErrorReport.isAllErrorsAtEof -> ReplCheckResult.Incomplete + syntaxErrorReport.isHasErrors -> ReplCheckResult.Error(errorHolder.renderedDiagnostics) + else -> ReplCheckResult.Ok } } } +} - fun eval(executionNumber: Long, code: String): EvalResult { + +abstract class GenericReplCompiler( + disposable: Disposable, + scriptDefinition: KotlinScriptDefinition, + compilerConfiguration: CompilerConfiguration, + messageCollector: MessageCollector +) : ReplCompiler, GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector) { + private val analyzerEngine = CliReplAnalyzerEngine(environment) + + private var lastDependencies: KotlinScriptExternalDependencies? = null + + private val descriptorsHistory = arrayListOf>() + + override fun compile(codeLine: ReplCodeLine, history: Iterable): ReplCompileResult { synchronized(this) { + + checkAndUpdateReplHistoryCollection(descriptorsHistory, history)?.let { + return@compile ReplCompileResult.HistoryMismatch(it) + } + val (psiFile, errorHolder) = run { - if (lineState == null || lineState!!.code != code) { - val res = checkComplete(executionNumber, code) - if (res != EvalResult.Ready) return@eval res + if (lineState == null || lineState!!.codeLine != codeLine) { + val res = check(codeLine, history) + when (res) { + ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete + is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message) + ReplCheckResult.Ok -> {} // continue + } } Pair(lineState!!.psiFile, lineState!!.errorHolder) } @@ -145,20 +142,17 @@ open class GenericRepl( val newCp = environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) if (newCp != null && newCp.isNotEmpty()) { logger.debug("new dependencies: $newCp") - classLoaderLock.write { - classpath.addAll(newCp) - classLoader = ReplClassLoader(URLClassLoader(newCp.map { it.toURI().toURL() }.toTypedArray(), classLoader)) - } + dependenciesAdded(newCp) } } if (lastDependencies != newDependencies) { lastDependencies = newDependencies } - val analysisResult = analyzerEngine.analyzeReplLine(psiFile, executionNumber.toInt()) + val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine.no) AnalyzerWithCompilerReport.Companion.reportDiagnostics(analysisResult.diagnostics, errorHolder, false) val scriptDescriptor = when (analysisResult) { - is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return EvalResult.Error.CompileTime(errorHolder.renderedDiagnostics) + is CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderedDiagnostics) is CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor else -> error("Unexpected result ${analysisResult.javaClass}") } @@ -172,7 +166,7 @@ open class GenericRepl( compilerConfiguration ) state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME - state.replSpecific.earlierScriptsForReplInterpreter = earlierLines.map(EarlierLine::getScriptDescriptor) + state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.map { it.second } state.beforeCompile() KotlinCodegenFacade.generatePackage( state, @@ -180,68 +174,44 @@ open class GenericRepl( setOf(psiFile.script!!.getContainingKtFile()), org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION) - for (outputFile in state.factory.asList()) { - if (outputFile.relativePath.endsWith(".class")) { - classLoaderLock.read { - classLoader.addClass(JvmClassName.byInternalName(outputFile.relativePath.replaceFirst("\\.class$".toRegex(), "")), - outputFile.asByteArray()) - } - } - } + descriptorsHistory.add(codeLine to scriptDescriptor) - try { - val scriptClass = classLoaderLock.read { classLoader.loadClass("Line$executionNumber") } - - val constructorParams = earlierLines.map(EarlierLine::getScriptClass).toTypedArray() - val constructorArgs = earlierLines.map(EarlierLine::getScriptInstance).toTypedArray() - - val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams) - val scriptInstance = - try { - evalWithIO { scriptInstanceConstructor.newInstance(*constructorArgs) } - } - catch (e: Throwable) { - // ignore everything in the stack trace until this constructor call - return EvalResult.Error.Runtime(renderStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.")) - } - - val rvField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true } - val rv: Any? = rvField.get(scriptInstance) - - earlierLines.add(EarlierLine(code, scriptDescriptor, scriptClass, scriptInstance)) - - return if (state.replSpecific.hasResult) EvalResult.ValueResult(rv) else EvalResult.UnitResult - } - catch (e: Throwable) { - throw e - } + return ReplCompileResult.CompiledClasses(state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) }, state.replSpecific.hasResult) } } - // override to capture output - open fun evalWithIO(body: () -> T): T = body() - companion object { private val SCRIPT_RESULT_FIELD_NAME = "\$\$result" + } +} - private fun renderStackTrace(cause: Throwable, startFromMethodName: String): String { - val newTrace = arrayListOf() - var skip = true - for ((i, element) in cause.stackTrace.withIndex().reversed()) { - if ("${element.className}.${element.methodName}" == startFromMethodName) { - skip = false - } - if (!skip) { - newTrace.add(element) + +open class GenericRepl( + disposable: Disposable, + scriptDefinition: KotlinScriptDefinition, + compilerConfiguration: CompilerConfiguration, + messageCollector: MessageCollector +) : ReplEvaluator, GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector) { + + private val compiledEvaluator = GenericReplCompiledEvaluator(classpath) + + override fun eval(codeLine: ReplCodeLine, history: Iterable): ReplEvalResult { + synchronized(this) { + + val (compiledClasses, hasResult) = compile(codeLine, history).let { + when (it) { + ReplCompileResult.Incomplete -> return@eval ReplEvalResult.Incomplete + is ReplCompileResult.HistoryMismatch -> return@eval ReplEvalResult.HistoryMismatch(it.lineNo) + is ReplCompileResult.Error -> return@eval ReplEvalResult.Error.CompileTime(it.message) + is ReplCompileResult.CompiledClasses -> it.classes to it.hasResult } } - val resultingTrace = newTrace.reversed().dropLast(1) - - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UsePropertyAccessSyntax") - (cause as java.lang.Throwable).setStackTrace(resultingTrace.toTypedArray()) - - return Throwables.getStackTraceAsString(cause) + return compiledEvaluator.eval(codeLine, history, compiledClasses, hasResult) } } + + override fun dependenciesAdded(classpath: List) { + compiledEvaluator.dependenciesAdded(classpath) + } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/KotlinJsr232ScriptEngine.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/KotlinJsr232ScriptEngine.kt index a5a8ff0c5f2..03edacecae4 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/KotlinJsr232ScriptEngine.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/KotlinJsr232ScriptEngine.kt @@ -21,6 +21,8 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine +import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.script.KotlinScriptDefinition import java.io.Reader @@ -72,20 +74,24 @@ class KotlinJsr232ScriptEngine( private val repl = object : GenericRepl(disposable, scriptDefinition, compilerConfiguration, messageCollector) {} - private var lineCount = 0L + private var lineCount = 0 + + private val history = arrayListOf() override fun eval(script: String, context: ScriptContext?): Any? { lineCount += 1 // TODO bind to context - val evalResult = repl.eval(lineCount, script) + val codeLine = ReplCodeLine(lineCount, script) + val evalResult = repl.eval(codeLine, history) messageCollector.resetAndThrowOnErrors() val ret = when (evalResult) { - is GenericRepl.EvalResult.ValueResult -> evalResult.value - is GenericRepl.EvalResult.UnitResult -> null - is GenericRepl.EvalResult.Error -> throw ScriptException(evalResult.errorText) - is GenericRepl.EvalResult.Incomplete -> throw ScriptException("error: incomplete code") - else -> throw ScriptException("Unexpected result from eval call: $evalResult") + is ReplEvalResult.ValueResult -> evalResult.value + is ReplEvalResult.UnitResult -> null + is ReplEvalResult.Error -> throw ScriptException(evalResult.message) + is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code") + is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${evalResult.lineNo}") } + history.add(codeLine) // TODO update context return ret } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt index 9cc9361913c..cdd43c8e371 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/ReplInterpreter.kt @@ -23,6 +23,7 @@ import com.intellij.psi.PsiFileFactory import com.intellij.psi.impl.PsiFileFactoryImpl import com.intellij.testFramework.LightVirtualFile import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport +import org.jetbrains.kotlin.cli.common.repl.ReplClassLoader import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots