Implement invokable/invoker interface on the generic repl evaluator, refactor reflection utils on the way
This commit is contained in:
@@ -16,72 +16,20 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.common
|
package org.jetbrains.kotlin.cli.common
|
||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.*
|
||||||
import kotlin.reflect.KParameter
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
import kotlin.reflect.KType
|
|
||||||
|
|
||||||
fun tryConstructScriptClass(scriptClass: Class<*>, scriptArgs: List<String>): Any? {
|
fun tryConstructClassFromStringArgs(scriptClass: Class<*>, scriptArgs: List<String>): Any? {
|
||||||
|
|
||||||
fun convertPrimitive(type: KType?, arg: String): Any? =
|
|
||||||
when (type?.classifier) {
|
|
||||||
String::class -> arg
|
|
||||||
Int::class -> arg.toInt()
|
|
||||||
Long::class -> arg.toLong()
|
|
||||||
Short::class -> arg.toShort()
|
|
||||||
Byte::class -> arg.toByte()
|
|
||||||
Char::class -> arg[0]
|
|
||||||
Float::class -> arg.toFloat()
|
|
||||||
Double::class -> arg.toDouble()
|
|
||||||
Boolean::class -> arg.toBoolean()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun convertArray(type: KType?, args: List<String>): Any? =
|
|
||||||
when (type?.classifier) {
|
|
||||||
String::class -> args.toTypedArray()
|
|
||||||
Int::class -> args.map(String::toInt).toTypedArray()
|
|
||||||
Long::class -> args.map(String::toLong).toTypedArray()
|
|
||||||
Short::class -> args.map(String::toShort).toTypedArray()
|
|
||||||
Byte::class -> args.map(String::toByte).toTypedArray()
|
|
||||||
Char::class -> args.map { it[0] }.toTypedArray()
|
|
||||||
Float::class -> args.map(String::toFloat).toTypedArray()
|
|
||||||
Double::class -> args.map(String::toDouble).toTypedArray()
|
|
||||||
Boolean::class -> args.map(String::toBoolean).toTypedArray()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun foldingFunc(state: Pair<List<Any>, List<String>?>, par: KParameter): Pair<List<Any>, List<String>?> {
|
|
||||||
state.second?.let { scriptArgsLeft ->
|
|
||||||
try {
|
|
||||||
if (scriptArgsLeft.isNotEmpty()) {
|
|
||||||
val primArgCandidate = convertPrimitive(par.type, scriptArgsLeft.first())
|
|
||||||
if (primArgCandidate != null)
|
|
||||||
return@foldingFunc Pair(state.first + primArgCandidate, scriptArgsLeft.drop(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((par.type.classifier as? KClass<*>)?.qualifiedName == Array<Any>::class.qualifiedName) {
|
|
||||||
val arrCompType = par.type.arguments.getOrNull(0)?.type
|
|
||||||
val arrayArgCandidate = convertArray(arrCompType, scriptArgsLeft)
|
|
||||||
if (arrayArgCandidate != null)
|
|
||||||
return@foldingFunc Pair(state.first + arrayArgCandidate, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (e: NumberFormatException) {
|
|
||||||
} // just skips to return below
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return scriptClass.getConstructor(Array<String>::class.java).newInstance(scriptArgs.toTypedArray())
|
return scriptClass.getConstructor(Array<String>::class.java).newInstance(scriptArgs.toTypedArray())
|
||||||
}
|
}
|
||||||
catch (e: NoSuchMethodException) {
|
catch (e: NoSuchMethodException) {
|
||||||
for (ctor in scriptClass.kotlin.constructors) {
|
for (ctor in scriptClass.kotlin.constructors) {
|
||||||
val (ctorArgs, scriptArgsLeft) = ctor.parameters.fold(Pair(emptyList<Any>(), scriptArgs), ::foldingFunc)
|
val mapping = tryCreateCallableMapping(ctor, scriptArgs, StringArgsConverter())
|
||||||
if (ctorArgs.size <= ctor.parameters.size && (scriptArgsLeft == null || scriptArgsLeft.isEmpty())) {
|
if (mapping != null) {
|
||||||
val argsMap = ctor.parameters.zip(ctorArgs).toMap()
|
|
||||||
try {
|
try {
|
||||||
return ctor.callBy(argsMap)
|
return ctor.callBy(mapping)
|
||||||
}
|
}
|
||||||
catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
|
catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
|
||||||
}
|
}
|
||||||
@@ -92,3 +40,105 @@ fun tryConstructScriptClass(scriptClass: Class<*>, scriptArgs: List<String>): An
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun tryCreateCallableMapping(callable: KCallable<*>, args: List<Any?>): Map<KParameter, Any?>? =
|
||||||
|
tryCreateCallableMapping(callable, args, AnyArgsConverter())
|
||||||
|
|
||||||
|
|
||||||
|
private interface ArgsConverter<T> {
|
||||||
|
|
||||||
|
data class Result(val v: Any?, val argsConsumed: Int)
|
||||||
|
|
||||||
|
fun convert(parameter: KParameter, args: List<T>, startArgIndex: Int): Result?
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T: Any?> tryCreateCallableMapping(callable: KCallable<*>, args: List<T>, converter: ArgsConverter<T>): Map<KParameter, Any?>? {
|
||||||
|
|
||||||
|
var argIdx = 0
|
||||||
|
val res = mutableMapOf<KParameter, Any?>()
|
||||||
|
for (par in callable.parameters) {
|
||||||
|
val (compatibleArg, argsConsumed) = converter.convert(par, args, argIdx) ?: return null
|
||||||
|
if (argsConsumed > 0) {
|
||||||
|
res.put(par, compatibleArg)
|
||||||
|
argIdx += argsConsumed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StringArgsConverter : ArgsConverter<String> {
|
||||||
|
|
||||||
|
override fun convert(parameter: KParameter, args: List<String>, startArgIndex: Int): ArgsConverter.Result? {
|
||||||
|
|
||||||
|
fun convertPrimitive(type: KType?, arg: String): Any? =
|
||||||
|
when (type?.classifier) {
|
||||||
|
String::class -> arg
|
||||||
|
Int::class -> arg.toInt()
|
||||||
|
Long::class -> arg.toLong()
|
||||||
|
Short::class -> arg.toShort()
|
||||||
|
Byte::class -> arg.toByte()
|
||||||
|
Char::class -> arg[0]
|
||||||
|
Float::class -> arg.toFloat()
|
||||||
|
Double::class -> arg.toDouble()
|
||||||
|
Boolean::class -> arg.toBoolean()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertArray(type: KType?, args: List<String>): Any? =
|
||||||
|
when (type?.classifier) {
|
||||||
|
String::class -> args.toTypedArray()
|
||||||
|
Int::class -> args.map(String::toInt).toTypedArray()
|
||||||
|
Long::class -> args.map(String::toLong).toTypedArray()
|
||||||
|
Short::class -> args.map(String::toShort).toTypedArray()
|
||||||
|
Byte::class -> args.map(String::toByte).toTypedArray()
|
||||||
|
Char::class -> args.map { it[0] }.toTypedArray()
|
||||||
|
Float::class -> args.map(String::toFloat).toTypedArray()
|
||||||
|
Double::class -> args.map(String::toDouble).toTypedArray()
|
||||||
|
Boolean::class -> args.map(String::toBoolean).toTypedArray()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (startArgIndex >= args.size && parameter.isOptional)
|
||||||
|
return ArgsConverter.Result(null, 0)
|
||||||
|
|
||||||
|
if (startArgIndex < args.size) {
|
||||||
|
val primArgCandidate = convertPrimitive(parameter.type, args[startArgIndex])
|
||||||
|
if (primArgCandidate != null)
|
||||||
|
return ArgsConverter.Result(primArgCandidate, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((parameter.type.classifier as? KClass<*>)?.qualifiedName == Array<Any>::class.qualifiedName) {
|
||||||
|
val arrCompType = parameter.type.arguments.getOrNull(0)?.type
|
||||||
|
val arrayArgCandidate = convertArray(arrCompType, args.drop(startArgIndex))
|
||||||
|
if (arrayArgCandidate != null)
|
||||||
|
return ArgsConverter.Result(arrayArgCandidate, args.size - startArgIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: NumberFormatException) {}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AnyArgsConverter : ArgsConverter<Any?> {
|
||||||
|
override fun convert(parameter: KParameter, args: List<Any?>, startArgIndex: Int): ArgsConverter.Result? {
|
||||||
|
|
||||||
|
fun convertSingle(type: KType, arg: Any?): Any? = when {
|
||||||
|
type.classifier == arg?.javaClass?.kotlin -> arg
|
||||||
|
type.jvmErasure.java.isAssignableFrom(arg?.javaClass) -> arg
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startArgIndex >= args.size && parameter.isOptional)
|
||||||
|
return ArgsConverter.Result(null, 0)
|
||||||
|
|
||||||
|
if (startArgIndex < args.size) {
|
||||||
|
val primArgCandidate = convertSingle(parameter.type, args[startArgIndex])
|
||||||
|
if (primArgCandidate != null)
|
||||||
|
return ArgsConverter.Result(primArgCandidate, 1)
|
||||||
|
}
|
||||||
|
// TODO: add vararg support
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+57
-7
@@ -16,24 +16,33 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.common.repl
|
package org.jetbrains.kotlin.cli.common.repl
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.tryCreateCallableMapping
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.concurrent.read
|
import kotlin.concurrent.read
|
||||||
import kotlin.concurrent.write
|
import kotlin.concurrent.write
|
||||||
|
import kotlin.reflect.*
|
||||||
|
import kotlin.reflect.jvm.javaType
|
||||||
|
|
||||||
open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClassloader: ClassLoader?, val scriptArgs: Array<Any?>? = null, val scriptArgsTypes: Array<Class<*>>? = null) : ReplCompiledEvaluator {
|
open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>,
|
||||||
|
baseClassloader: ClassLoader?,
|
||||||
|
val scriptArgs: Array<Any?>? = null,
|
||||||
|
val scriptArgsTypes: Array<Class<*>>? = null
|
||||||
|
) : ReplCompiledEvaluator, ReplInvoker {
|
||||||
|
|
||||||
private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
private var classLoader: org.jetbrains.kotlin.cli.common.repl.ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||||
private val classLoaderLock = ReentrantReadWriteLock()
|
private val classLoaderLock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
private class ClassWithInstance(val klass: Class<*>, val instance: Any)
|
// TODO: consider to expose it as a part of (evaluator, invoker) interface
|
||||||
|
private val evalStateLock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
|
private data class ClassWithInstance(val klass: Class<*>, val instance: Any)
|
||||||
|
|
||||||
private val compiledLoadedClassesHistory = ReplHistory<ClassWithInstance>()
|
private val compiledLoadedClassesHistory = ReplHistory<ClassWithInstance>()
|
||||||
|
|
||||||
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, compiledClasses: List<CompiledClassData>, hasResult: Boolean, classpathAddendum: List<File>): ReplEvalResult {
|
override fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, compiledClasses: List<CompiledClassData>, hasResult: Boolean, classpathAddendum: List<File>): ReplEvalResult /*= evalStateLock.write*/ {
|
||||||
|
|
||||||
checkAndUpdateReplHistoryCollection(compiledLoadedClassesHistory, history)?.let {
|
checkAndUpdateReplHistoryCollection(compiledLoadedClassesHistory, history)?.let {
|
||||||
return@eval ReplEvalResult.HistoryMismatch(compiledLoadedClassesHistory.lines, it)
|
return@eval ReplEvalResult.HistoryMismatch(compiledLoadedClassesHistory.lines, it)
|
||||||
}
|
}
|
||||||
@@ -62,8 +71,9 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
|
|||||||
try {
|
try {
|
||||||
classLoader.loadClass(mainLineClassName!!)
|
classLoader.loadClass(mainLineClassName!!)
|
||||||
}
|
}
|
||||||
catch (e: Exception) {
|
catch (e: Throwable) {
|
||||||
throw Exception("Error loading class $mainLineClassName: known classes: ${compiledClasses.map { classNameFromPath(it.path).fqNameForClassNameWithoutDollars.asString() }}", e)
|
return ReplEvalResult.Error.Runtime(compiledLoadedClassesHistory.lines, "Error loading class $mainLineClassName: known classes: ${compiledClasses.map { classNameFromPath(it.path).fqNameForClassNameWithoutDollars.asString() }}",
|
||||||
|
e as? Exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +90,7 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
|
|||||||
}
|
}
|
||||||
catch (e: Throwable) {
|
catch (e: Throwable) {
|
||||||
// ignore everything in the stack trace until this constructor call
|
// ignore everything in the stack trace until this constructor call
|
||||||
return ReplEvalResult.Error.Runtime(compiledLoadedClassesHistory.lines, renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"))
|
return ReplEvalResult.Error.Runtime(compiledLoadedClassesHistory.lines, renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
compiledLoadedClassesHistory.add(codeLine, ClassWithInstance(scriptClass, scriptInstance))
|
compiledLoadedClassesHistory.add(codeLine, ClassWithInstance(scriptClass, scriptInstance))
|
||||||
@@ -91,6 +101,38 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
|
|||||||
return if (hasResult) ReplEvalResult.ValueResult(compiledLoadedClassesHistory.lines, rv) else ReplEvalResult.UnitResult(compiledLoadedClassesHistory.lines)
|
return if (hasResult) ReplEvalResult.ValueResult(compiledLoadedClassesHistory.lines, rv) else ReplEvalResult.UnitResult(compiledLoadedClassesHistory.lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun <T: Any> getInterface(klass: KClass<T>): ReplInvokeResult = evalStateLock.read {
|
||||||
|
val (_, instance) = compiledLoadedClassesHistory.values.lastOrNull() ?: return ReplInvokeResult.Error.NoSuchEntity("no script ")
|
||||||
|
return getInterface(instance, klass)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T: Any> getInterface(receiver: Any, klass: KClass<T>): ReplInvokeResult = evalStateLock.read {
|
||||||
|
return ReplInvokeResult.ValueResult(klass.safeCast(receiver))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invokeMethod(receiver: Any, name: String, vararg args: Any?): ReplInvokeResult = evalStateLock.read {
|
||||||
|
return invokeImpl(receiver.javaClass.kotlin, receiver, name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invokeFunction(name: String, vararg args: Any?): ReplInvokeResult = evalStateLock.read {
|
||||||
|
val (klass, instance) = compiledLoadedClassesHistory.values.lastOrNull() ?: return ReplInvokeResult.Error.NoSuchEntity("no script ")
|
||||||
|
return invokeImpl(klass.kotlin, instance, name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun invokeImpl(receiverClass: KClass<*>, receiverInstance: Any, name: String, args: Array<out Any?>): ReplInvokeResult {
|
||||||
|
val (fn, mapping) = receiverClass.functions.filter { it.name == name }.findMapping(args.toList()) ?:
|
||||||
|
receiverClass.memberExtensionFunctions.filter { it.name == name }.findMapping(listOf<Any?>(receiverInstance) + args) ?:
|
||||||
|
return ReplInvokeResult.Error.NoSuchEntity("no suitable function '$name' found")
|
||||||
|
val res = try {
|
||||||
|
evalWithIO { fn.callBy(mapping) }
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
// ignore everything in the stack trace until this constructor call
|
||||||
|
return ReplInvokeResult.Error.Runtime(renderReplStackTrace(e.cause!!, startFromMethodName = "${fn.name}"), e as? Exception)
|
||||||
|
}
|
||||||
|
return if (fn.returnType.classifier == Unit::class) ReplInvokeResult.UnitResult else ReplInvokeResult.ValueResult(res)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||||
}
|
}
|
||||||
@@ -98,3 +140,11 @@ open class GenericReplCompiledEvaluator(baseClasspath: Iterable<File>, baseClass
|
|||||||
|
|
||||||
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
||||||
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
||||||
|
|
||||||
|
private fun Iterable<KFunction<*>>.findMapping(args: List<Any?>): Pair<KFunction<*>, Map<KParameter, Any?>>? {
|
||||||
|
for (fn in this) {
|
||||||
|
val mapping = tryCreateCallableMapping(fn, args)
|
||||||
|
if (mapping != null) return fn to mapping
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ import javax.script.*
|
|||||||
//
|
//
|
||||||
//val Bindings.kotlinScriptHistory:
|
//val Bindings.kotlinScriptHistory:
|
||||||
|
|
||||||
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable, Invocable {
|
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
||||||
protected var lineCount = 0
|
protected var lineCount = 0
|
||||||
|
|
||||||
protected val history = arrayListOf<ReplCodeLine>()
|
protected val history = arrayListOf<ReplCodeLine>()
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -27,6 +28,8 @@ data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: consider storing code hash where source is not needed
|
||||||
|
|
||||||
data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializable {
|
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 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)
|
override fun hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes)
|
||||||
@@ -70,6 +73,22 @@ sealed class ReplCompileResult(val updatedHistory: List<ReplCodeLine>) : Seriali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class ReplInvokeResult : Serializable {
|
||||||
|
class ValueResult(val value: Any?) : ReplInvokeResult() {
|
||||||
|
override fun toString(): String = "Result: $value"
|
||||||
|
}
|
||||||
|
object UnitResult : ReplInvokeResult()
|
||||||
|
sealed class Error(val message: String) : ReplInvokeResult() {
|
||||||
|
class Runtime(message: String, val cause: Exception? = null) : Error(message)
|
||||||
|
class NoSuchEntity(message: String) : Error(message)
|
||||||
|
class CompileTime(message: String, val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(message)
|
||||||
|
override fun toString(): String = "${this::class.simpleName}Error(message = \"$message\""
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
private val serialVersionUID: Long = 8228357578L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed class ReplEvalResult(val updatedHistory: List<ReplCodeLine>) : Serializable {
|
sealed class ReplEvalResult(val updatedHistory: List<ReplCodeLine>) : Serializable {
|
||||||
class ValueResult(updatedHistory: List<ReplCodeLine>, val value: Any?) : ReplEvalResult(updatedHistory) {
|
class ValueResult(updatedHistory: List<ReplCodeLine>, val value: Any?) : ReplEvalResult(updatedHistory) {
|
||||||
override fun toString(): String = "Result: $value"
|
override fun toString(): String = "Result: $value"
|
||||||
@@ -78,7 +97,7 @@ sealed class ReplEvalResult(val updatedHistory: List<ReplCodeLine>) : Serializab
|
|||||||
class Incomplete(updatedHistory: List<ReplCodeLine>) : ReplEvalResult(updatedHistory)
|
class Incomplete(updatedHistory: List<ReplCodeLine>) : ReplEvalResult(updatedHistory)
|
||||||
class HistoryMismatch(updatedHistory: List<ReplCodeLine>, val lineNo: Int): ReplEvalResult(updatedHistory)
|
class HistoryMismatch(updatedHistory: List<ReplCodeLine>, val lineNo: Int): ReplEvalResult(updatedHistory)
|
||||||
sealed class Error(updatedHistory: List<ReplCodeLine>, val message: String) : ReplEvalResult(updatedHistory) {
|
sealed class Error(updatedHistory: List<ReplCodeLine>, val message: String) : ReplEvalResult(updatedHistory) {
|
||||||
class Runtime(updatedHistory: List<ReplCodeLine>, message: String) : Error(updatedHistory, message)
|
class Runtime(updatedHistory: List<ReplCodeLine>, message: String, val cause: Exception? = null) : Error(updatedHistory, message)
|
||||||
class CompileTime(updatedHistory: List<ReplCodeLine>,
|
class CompileTime(updatedHistory: List<ReplCodeLine>,
|
||||||
message: String,
|
message: String,
|
||||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION
|
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION
|
||||||
@@ -98,6 +117,13 @@ interface ReplCompiler : ReplChecker {
|
|||||||
fun compile(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCompileResult
|
fun compile(codeLine: ReplCodeLine, history: List<ReplCodeLine>): ReplCompileResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ReplInvoker {
|
||||||
|
fun <T: Any> getInterface(clasz: KClass<T>): ReplInvokeResult
|
||||||
|
fun <T: Any> getInterface(receiver: Any, clasz: KClass<T>): ReplInvokeResult
|
||||||
|
fun invokeMethod(receiver: Any, name: String, vararg args: Any?): ReplInvokeResult
|
||||||
|
fun invokeFunction(name: String, vararg args: Any?): ReplInvokeResult
|
||||||
|
}
|
||||||
|
|
||||||
interface ReplCompiledEvaluator {
|
interface ReplCompiledEvaluator {
|
||||||
|
|
||||||
fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, compiledClasses: List<CompiledClassData>, hasResult: Boolean, classpathAddendum: List<File>): ReplEvalResult
|
fun eval(codeLine: ReplCodeLine, history: List<ReplCodeLine>, compiledClasses: List<CompiledClassData>, hasResult: Boolean, classpathAddendum: List<File>): ReplEvalResult
|
||||||
|
|||||||
+2
-2
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
|||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
import org.jetbrains.kotlin.cli.common.output.outputUtils.writeAll
|
||||||
import org.jetbrains.kotlin.cli.common.tryConstructScriptClass
|
import org.jetbrains.kotlin.cli.common.tryConstructClassFromStringArgs
|
||||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||||
@@ -227,7 +227,7 @@ object KotlinToJVMBytecodeCompiler {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
tryConstructScriptClass(scriptClass, scriptArgs)
|
tryConstructClassFromStringArgs(scriptClass, scriptArgs)
|
||||||
?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n")
|
?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n")
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.scripts
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.tryConstructScriptClass
|
import org.jetbrains.kotlin.cli.common.tryConstructClassFromStringArgs
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||||
@@ -214,7 +214,7 @@ class ScriptTemplateTest {
|
|||||||
val aClass = compileScript("fib.kts", ScriptWithIntParam::class)
|
val aClass = compileScript("fib.kts", ScriptWithIntParam::class)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
captureOut {
|
captureOut {
|
||||||
val anObj = tryConstructScriptClass(aClass!!, listOf("4"))
|
val anObj = tryConstructClassFromStringArgs(aClass!!, listOf("4"))
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
}.let {
|
}.let {
|
||||||
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, it)
|
assertEqualsTrimmed(NUM_4_LINE + FIB_SCRIPT_OUTPUT_TAIL, it)
|
||||||
@@ -227,7 +227,7 @@ class ScriptTemplateTest {
|
|||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
var exceptionThrown = false
|
var exceptionThrown = false
|
||||||
try {
|
try {
|
||||||
tryConstructScriptClass(aClass!!, emptyList())
|
tryConstructClassFromStringArgs(aClass!!, emptyList())
|
||||||
}
|
}
|
||||||
catch (e: InvocationTargetException) {
|
catch (e: InvocationTargetException) {
|
||||||
Assert.assertTrue(e.cause is IllegalStateException)
|
Assert.assertTrue(e.cause is IllegalStateException)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.scripts
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.tryConstructScriptClass
|
import org.jetbrains.kotlin.cli.common.tryConstructClassFromStringArgs
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
||||||
@@ -44,7 +44,7 @@ class ScriptTest : KtUsefulTestCase() {
|
|||||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
val out = captureOut {
|
val out = captureOut {
|
||||||
val anObj = tryConstructScriptClass(aClass!!, listOf("4", "comment"))
|
val anObj = tryConstructClassFromStringArgs(aClass!!, listOf("4", "comment"))
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
}
|
}
|
||||||
assertEqualsTrimmed(NUM_4_LINE + " (comment)" + FIB_SCRIPT_OUTPUT_TAIL, out)
|
assertEqualsTrimmed(NUM_4_LINE + " (comment)" + FIB_SCRIPT_OUTPUT_TAIL, out)
|
||||||
@@ -55,7 +55,7 @@ class ScriptTest : KtUsefulTestCase() {
|
|||||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
val out = captureOut {
|
val out = captureOut {
|
||||||
val anObj = tryConstructScriptClass(aClass!!, emptyList())
|
val anObj = tryConstructClassFromStringArgs(aClass!!, emptyList())
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
}
|
}
|
||||||
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out)
|
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out)
|
||||||
@@ -68,7 +68,7 @@ class ScriptTest : KtUsefulTestCase() {
|
|||||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, saveClassesDir = tmpdir)
|
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, saveClassesDir = tmpdir)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
val out1 = captureOut {
|
val out1 = captureOut {
|
||||||
val anObj = tryConstructScriptClass(aClass!!, emptyList())
|
val anObj = tryConstructClassFromStringArgs(aClass!!, emptyList())
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
}
|
}
|
||||||
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out1)
|
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out1)
|
||||||
@@ -76,7 +76,7 @@ class ScriptTest : KtUsefulTestCase() {
|
|||||||
val aClassSaved = savedClassLoader.loadClass(aClass.name)
|
val aClassSaved = savedClassLoader.loadClass(aClass.name)
|
||||||
Assert.assertNotNull(aClassSaved)
|
Assert.assertNotNull(aClassSaved)
|
||||||
val out2 = captureOut {
|
val out2 = captureOut {
|
||||||
val anObjSaved = tryConstructScriptClass(aClassSaved!!, emptyList())
|
val anObjSaved = tryConstructClassFromStringArgs(aClassSaved!!, emptyList())
|
||||||
Assert.assertNotNull(anObjSaved)
|
Assert.assertNotNull(anObjSaved)
|
||||||
}
|
}
|
||||||
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out2)
|
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out2)
|
||||||
|
|||||||
+1
-1
@@ -202,7 +202,7 @@ public class ExecuteKotlinScriptMojo extends AbstractMojo {
|
|||||||
try {
|
try {
|
||||||
Class<?> klass = classLoader.loadClass(nameForScript.asString());
|
Class<?> klass = classLoader.loadClass(nameForScript.asString());
|
||||||
ExecuteKotlinScriptMojo.INSTANCE = this;
|
ExecuteKotlinScriptMojo.INSTANCE = this;
|
||||||
if (ReflectionUtilKt.tryConstructScriptClass(klass, scriptArguments) == null)
|
if (ReflectionUtilKt.tryConstructClassFromStringArgs(klass, scriptArguments) == null)
|
||||||
throw new ScriptExecutionException(scriptFile, "unable to construct script");
|
throw new ScriptExecutionException(scriptFile, "unable to construct script");
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new ScriptExecutionException(scriptFile, "internal error", e);
|
throw new ScriptExecutionException(scriptFile, "internal error", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user