Avoid using reflected types in the scripting API

since it causes numerous classloading issues. Using the wrapping types
and reload them in the proper context when needed.
Note: this version supports only classes, but the wrapping type could
be extended to support other types in the future.
+ numerous fixes related to proper loading and handling of the templates.
This commit is contained in:
Ilya Chernikov
2018-05-22 19:22:42 +02:00
parent 4d65f0478b
commit a46dd5b30e
28 changed files with 506 additions and 185 deletions
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.script.experimental.jvm
import java.net.URLClassLoader
import kotlin.reflect.KClass
import kotlin.script.experimental.api.*
class JvmGetScriptingClass : GetScriptingClass {
private var dependencies: List<ScriptDependency>? = null
private var classLoader: ClassLoader? = null
private var baseClassLoader: ClassLoader? = null
@Synchronized
override fun invoke(classType: KotlinType, contextClass: KClass<*>, environment: ScriptingEnvironment): KClass<*> {
// checking if class already loaded in the same context
val contextClassloader = contextClass.java.classLoader
if (classType.fromClass != null) {
if (classType.fromClass!!.java.classLoader == null) return classType.fromClass!! // root classloader
val actualClassLoadersChain = generateSequence(classType.fromClass!!.java.classLoader) { it.parent }
if (actualClassLoadersChain.any { it == contextClassloader }) return classType.fromClass!!
}
val newDeps = environment.getOrNull(ScriptingEnvironmentProperties.configurationDependencies)
if (dependencies == null) {
dependencies = newDeps
} else {
if (newDeps != dependencies) throw IllegalArgumentException("scripting environment dependencies changed")
}
if (baseClassLoader == null) {
baseClassLoader = contextClassloader
} else {
val baseClassLoadersChain = generateSequence(baseClassLoader) { it.parent }
if (baseClassLoadersChain.none { it == contextClassloader }) throw IllegalArgumentException("scripting class instantiation context changed")
}
if (classLoader == null) {
val classpath = dependencies?.flatMap {
when(it) {
is JvmDependency -> it.classpath.map { it.toURI().toURL() }
else -> throw IllegalArgumentException("unknown dependency type $it")
}
}
classLoader =
if (classpath == null || classpath.isEmpty()) baseClassLoader
else URLClassLoader(classpath.toTypedArray(), baseClassLoader)
}
return try {
classLoader!!.loadClass(classType.typeName).kotlin
} catch (e: Throwable) {
throw IllegalArgumentException("unable to load class $classType", e)
}
}
}