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
@@ -33,7 +33,6 @@ import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.scripting.compiler.plugin.KotlinScriptDefinitionAdapterFromNewAPI
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil.isInContent
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.KotlinScriptDefinition
@@ -49,12 +48,15 @@ import java.net.URLClassLoader
import kotlin.concurrent.write
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.api.ScriptingEnvironment
import kotlin.script.experimental.api.ScriptingEnvironmentProperties
import kotlin.script.experimental.definitions.ScriptDefinitionFromAnnotatedBaseClass
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.JvmGetScriptingClass
import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.templates.standard.ScriptTemplateWithArgs
@@ -161,15 +163,17 @@ fun loadDefinitionsFromTemplates(
* i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on.
*/
additionalResolverClasspath: List<File> = emptyList()
): List<KotlinScriptDefinition> = try {
): List<KotlinScriptDefinition> {
val classpath = templateClasspath + additionalResolverClasspath
LOG.info("[kts] loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}")
val baseLoader = ScriptDefinitionContributor::class.java.classLoader
val loader = if (classpath.isEmpty()) baseLoader else URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), baseLoader)
templateClassNames.mapNotNull {
return templateClassNames.mapNotNull { templateClassName ->
try {
val template = loader.loadClass(it).kotlin
// TODO: drop class loading here - it should be handled downstream
// as a compatibility measure, the asm based reading of annotations should be implemented to filter classes before classloading
val template = loader.loadClass(templateClassName).kotlin
when {
template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.ScriptTemplateDefinition>() != null ||
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() != null -> {
@@ -181,7 +185,13 @@ fun loadDefinitionsFromTemplates(
}
template.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null -> {
KotlinScriptDefinitionAdapterFromNewAPI(
ScriptDefinitionFromAnnotatedBaseClass(ScriptingEnvironment(ScriptingEnvironmentProperties.baseClass to template))
ScriptDefinitionFromAnnotatedBaseClass(
ScriptingEnvironment(
ScriptingEnvironmentProperties.baseClass to KotlinType(template),
ScriptingEnvironmentProperties.configurationDependencies to listOf(JvmDependency(classpath)),
ScriptingEnvironmentProperties.getScriptingClass to JvmGetScriptingClass()
)
)
)
}
else -> {
@@ -192,19 +202,13 @@ fun loadDefinitionsFromTemplates(
} catch (e: ClassNotFoundException) {
// Assuming that direct ClassNotFoundException is the result of versions mismatch and missing subsystems, e.g. gradle
// so, it only results in warning, while other errors are severe misconfigurations, resulting it user-visible error
LOG.warn("[kts] cannot load script definition class $it", e)
LOG.warn("[kts] cannot load script definition class $templateClassName", e)
null
} catch (e: NoClassDefFoundError) {
LOG.error("[kts] cannot load script definition class $it", e)
null
} catch (e: InvocationTargetException) {
LOG.error("[kts] cannot load script definition class $it", e)
} catch (e: Throwable) {
LOG.error("[kts] cannot load script definition class $templateClassName", e)
null
}
}
} catch (ex: Throwable) {
// TODO: review exception handling
emptyList()
}
interface ScriptDefinitionContributor {