diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt index 3cd9cc81fb9..c5d0e380ce8 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.compiler.plugin.* import org.jetbrains.kotlin.config.CompilerConfiguration import java.io.File import java.net.URL -import java.net.URLClassLoader import java.util.* object PluginCliParser { diff --git a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt index 03e976f6e70..3058934c595 100644 --- a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt +++ b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt @@ -41,7 +41,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( val environment: Map? = null, val templateClasspath: List = emptyList() ) : KotlinScriptDefinition(template) { - val scriptFilePattern by lazy { + val scriptFilePattern by lazy(LazyThreadSafetyMode.PUBLICATION) { val pattern = takeUnlessError { val ann = template.annotations.firstIsInstanceOrNull() @@ -52,7 +52,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( Regex(pattern) } - override val dependencyResolver: DependenciesResolver by lazy { + override val dependencyResolver: DependenciesResolver by lazy(LazyThreadSafetyMode.PUBLICATION) { resolverFromAnnotation(template) ?: resolverFromLegacyAnnotation(template) ?: DependenciesResolver.NoDependencies @@ -99,12 +99,12 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( } } - private val samWithReceiverAnnotations: List? by lazy { + private val samWithReceiverAnnotations: List? by lazy(LazyThreadSafetyMode.PUBLICATION) { takeUnlessError { template.annotations.firstIsInstanceOrNull()?.annotations?.toList() } ?: takeUnlessError { template.annotations.firstIsInstanceOrNull()?.annotations?.toList() } } - override val acceptedAnnotations: List> by lazy { + override val acceptedAnnotations: List> by lazy(LazyThreadSafetyMode.PUBLICATION) { fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean = left.name == right.name && @@ -134,7 +134,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( } } - override val scriptExpectedLocations: List by lazy { + override val scriptExpectedLocations: List by lazy(LazyThreadSafetyMode.PUBLICATION) { takeUnlessError { template.annotations.firstIsInstanceOrNull() }?.value?.toList() ?: super.scriptExpectedLocations @@ -153,7 +153,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( override val annotationsForSamWithReceivers: List get() = samWithReceiverAnnotations ?: super.annotationsForSamWithReceivers - override val additionalCompilerArguments: Iterable? by lazy { + override val additionalCompilerArguments: Iterable? by lazy(LazyThreadSafetyMode.PUBLICATION) { takeUnlessError { template.annotations.firstIsInstanceOrNull()?.let { val res = it.provider.primaryConstructor?.call(it.arguments.asIterable()) diff --git a/compiler/frontend.script/src/org/jetbrains/kotlin/script/ScriptContentLoader.kt b/compiler/frontend.script/src/org/jetbrains/kotlin/script/ScriptContentLoader.kt index 3d3b24f2651..b99801fe61f 100644 --- a/compiler/frontend.script/src/org/jetbrains/kotlin/script/ScriptContentLoader.kt +++ b/compiler/frontend.script/src/org/jetbrains/kotlin/script/ScriptContentLoader.kt @@ -58,8 +58,8 @@ class ScriptContentLoader(private val project: Project) { class BasicScriptContents(virtualFile: VirtualFile, getAnnotations: () -> Iterable) : ScriptContents { override val file: File = File(virtualFile.path) - override val annotations: Iterable by lazy { getAnnotations() } - override val text: CharSequence? by lazy { virtualFile.inputStream.reader(charset = virtualFile.charset).readText() } + override val annotations: Iterable by lazy(LazyThreadSafetyMode.PUBLICATION) { getAnnotations() } + override val text: CharSequence? by lazy(LazyThreadSafetyMode.PUBLICATION) { virtualFile.inputStream.reader(charset = virtualFile.charset).readText() } } fun loadContentsAndResolveDependencies( diff --git a/libraries/scripting/common/src/kotlin/script/experimental/api/kotlinType.kt b/libraries/scripting/common/src/kotlin/script/experimental/api/kotlinType.kt index 20360891c84..4a7c8a1c2a5 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/api/kotlinType.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/api/kotlinType.kt @@ -8,13 +8,27 @@ package kotlin.script.experimental.api import kotlin.reflect.KClass import kotlin.reflect.KType -class KotlinType( +/** + * A Kotlin type representation for using in the scripting API + */ +class KotlinType private constructor( val typeName: String, - val fromClass: KClass<*>? = null + val fromClass: KClass<*>? // TODO: copy properties from KType ) { - // TODO: implement other approach for non-class types - constructor(type: KType) : this((type.classifier as KClass<*>).qualifiedName!!, type.classifier as KClass<*>) + /** + * Constructs KotlinType from fully-qualified [qualifiedTypeName] in a dot-separated form, e.g. "org.acme.Outer.Inner" + */ + constructor(qualifiedTypeName: String) : this(qualifiedTypeName, null) + /** + * Constructs KotlinType from reflected [kclass] + */ constructor(kclass: KClass<*>) : this(kclass.qualifiedName!!, kclass) + + // TODO: implement other approach for non-class types + /** + * Constructs KotlinType from reflected [ktype] + */ + constructor(type: KType) : this(type.classifier as KClass<*>) } diff --git a/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt b/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt index ff6c05ee75f..787473c86a9 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt @@ -15,7 +15,7 @@ private const val ILLEGAL_CONFIG_ANN_ARG = open class AnnotationsBasedCompilationConfigurator(val environment: ScriptingEnvironment) : ScriptCompilationConfigurator { - override val defaultConfiguration by lazy { + override val defaultConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) { val baseClass = environment.getScriptBaseClass(this) val cfg = baseClass.annotations.filterIsInstance(KotlinScriptDefaultCompilationConfiguration::class.java).flatMap { ann -> val params = try { diff --git a/libraries/scripting/common/src/kotlin/script/experimental/definitions/definitionFromAnnotation.kt b/libraries/scripting/common/src/kotlin/script/experimental/definitions/definitionFromAnnotation.kt index 4137a85de96..0cd44d0b5fe 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/definitions/definitionFromAnnotation.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/definitions/definitionFromAnnotation.kt @@ -19,7 +19,7 @@ private const val ERROR_MSG_PREFIX = "Unable to construct script definition: " open class ScriptDefinitionFromAnnotatedBaseClass(val environment: ScriptingEnvironment) : ScriptDefinition { private val getScriptingClass = environment.getOrNull(ScriptingEnvironmentProperties.getScriptingClass) - ?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting 'getClass' parameter in the scripting environment") + ?: throw IllegalArgumentException("${ERROR_MSG_PREFIX}Expecting 'getScriptingClass' parameter in the scripting environment") private val baseClass: KClass<*> = run { val baseClassType = environment.getOrNull(ScriptingEnvironmentProperties.baseClass) diff --git a/libraries/scripting/common/src/kotlin/script/experimental/util/propertyBag.kt b/libraries/scripting/common/src/kotlin/script/experimental/util/propertyBag.kt index 1f4ee38abcb..ee803841e59 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/util/propertyBag.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/util/propertyBag.kt @@ -33,13 +33,7 @@ open class ChainedPropertyBag private constructor(private val parent: ChainedPro inline operator fun get(key: TypedKey): T = getRaw(key) as T - fun getRaw(key: TypedKey): Any? = - when { - data.containsKey(key) -> data[key] - parent != null -> parent.getRaw(key) - key.defaultValue != null -> key.defaultValue - else -> throw IllegalArgumentException("Unknown key $key") - } + fun getRaw(key: TypedKey): Any? = getOrNullRaw(key) ?: throw IllegalArgumentException("Unknown key $key") inline fun getOrNull(key: TypedKey): T? = getOrNullRaw(key)?.let { it as T } diff --git a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptEnvironment.kt b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptEnvironment.kt index 4eca3aa3dbb..e63e006c417 100644 --- a/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptEnvironment.kt +++ b/libraries/scripting/jvm/src/kotlin/script/experimental/jvm/jvmScriptEnvironment.kt @@ -13,6 +13,7 @@ class JvmGetScriptingClass : GetScriptingClass { private var dependencies: List? = null private var classLoader: ClassLoader? = null + private var baseClassLoaderIsInitialized = false private var baseClassLoader: ClassLoader? = null @Synchronized @@ -20,10 +21,11 @@ class JvmGetScriptingClass : GetScriptingClass { // 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 fromClass = classType.fromClass + if (fromClass != null) { + if (fromClass.java.classLoader == null) return fromClass // root classloader + val actualClassLoadersChain = generateSequence(contextClassloader) { it.parent } + if (actualClassLoadersChain.any { it == fromClass.java.classLoader }) return fromClass } val newDeps = environment.getOrNull(ScriptingEnvironmentProperties.configurationDependencies) @@ -33,9 +35,10 @@ class JvmGetScriptingClass : GetScriptingClass { if (newDeps != dependencies) throw IllegalArgumentException("scripting environment dependencies changed") } - if (baseClassLoader == null) { + if (!baseClassLoaderIsInitialized) { baseClassLoader = contextClassloader - } else { + baseClassLoaderIsInitialized = true + } else if (baseClassLoader != null) { val baseClassLoadersChain = generateSequence(baseClassLoader) { it.parent } if (baseClassLoadersChain.none { it == contextClassloader }) throw IllegalArgumentException("scripting class instantiation context changed") } diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt index 58bea68f21a..85473bb0b20 100644 --- a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt @@ -26,7 +26,7 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit protected abstract val scriptFileExtensionWithDot: String - open val baseClass: KClass<*> by lazy { + open val baseClass: KClass<*> by lazy(LazyThreadSafetyMode.PUBLICATION) { getScriptingClass(scriptDefinition.compilationConfigurator.defaultConfiguration[ScriptingEnvironmentProperties.baseClass]) } @@ -48,33 +48,30 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit override val annotationsForSamWithReceivers: List get() = emptyList() - override val dependencyResolver: DependenciesResolver by lazy { + override val dependencyResolver: DependenciesResolver by lazy(LazyThreadSafetyMode.PUBLICATION) { BridgeDependenciesResolver(scriptDefinition.compilationConfigurator) } - override val acceptedAnnotations: List> by lazy { - val annNames = - scriptDefinition.compilationConfigurator.defaultConfiguration.getOrNull(ScriptCompileConfigurationProperties.refineConfigurationOnAnnotations) - ?: emptyList() - annNames.map { getScriptingClass(it) as KClass } + override val acceptedAnnotations: List> by lazy(LazyThreadSafetyMode.PUBLICATION) { + scriptDefinition.compilationConfigurator.defaultConfiguration.getOrNull(ScriptCompileConfigurationProperties.refineConfigurationOnAnnotations) + .orEmpty() + .map { getScriptingClass(it) as KClass } } - override val implicitReceivers: List by lazy { - val rcNames = - scriptDefinition.compilationConfigurator.defaultConfiguration.getOrNull(ScriptCompileConfigurationProperties.scriptImplicitReceivers) - ?: emptyList() - rcNames.map { getScriptingClass(it).starProjectedType } + override val implicitReceivers: List by lazy(LazyThreadSafetyMode.PUBLICATION) { + scriptDefinition.compilationConfigurator.defaultConfiguration.getOrNull(ScriptCompileConfigurationProperties.scriptImplicitReceivers) + .orEmpty() + .map { getScriptingClass(it).starProjectedType } } - override val environmentVariables: List> by lazy { + override val environmentVariables: List> by lazy(LazyThreadSafetyMode.PUBLICATION) { scriptDefinition.compilationConfigurator.defaultConfiguration.getOrNull(ScriptCompileConfigurationProperties.contextVariables) - ?.map { (k, v) -> k to getScriptingClass(v).starProjectedType } - ?: emptyList() + ?.map { (k, v) -> k to getScriptingClass(v).starProjectedType }.orEmpty() } override val additionalCompilerArguments: List get() = scriptDefinition.compilationConfigurator.defaultConfiguration.getOrNull(ScriptCompileConfigurationProperties.compilerOptions) - ?: emptyList() + .orEmpty() override val scriptExpectedLocations: List = listOf( @@ -82,7 +79,7 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit ScriptExpectedLocation.TestsOnly ) - private val scriptingClassGetter by lazy { + private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) { scriptDefinition.properties.getOrNull(ScriptingEnvironmentProperties.getScriptingClass) ?: throw IllegalArgumentException("Expecting 'getScriptingClass' property in the scripting environment") } diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/LazyScriptDefinitionFromDiscoveredClass.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/LazyScriptDefinitionFromDiscoveredClass.kt index 38fd2ba7a61..4148cd8c9f2 100644 --- a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/LazyScriptDefinitionFromDiscoveredClass.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/LazyScriptDefinitionFromDiscoveredClass.kt @@ -34,7 +34,7 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor( messageCollector: MessageCollector ) : this(loadAnnotationsFromClass(classBytes), className, classpath, messageCollector) - override val scriptDefinition: ScriptDefinition by lazy { + override val scriptDefinition: ScriptDefinition by lazy(LazyThreadSafetyMode.PUBLICATION) { messageCollector.report( CompilerMessageSeverity.LOGGING, "Configure scripting: loading script definition class $className using classpath $classpath\n. ${Thread.currentThread().stackTrace}" @@ -59,7 +59,7 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor( } } - override val scriptFileExtensionWithDot: String by lazy { + override val scriptFileExtensionWithDot: String by lazy(LazyThreadSafetyMode.PUBLICATION) { val ext = annotationsFromAsm.find { it.name == KotlinScriptFileExtension::class.simpleName!! }?.args?.first() ?: scriptDefinition.properties.let { it.getOrNull(ScriptDefinitionProperties.fileExtension) ?: "kts" @@ -67,7 +67,7 @@ class LazyScriptDefinitionFromDiscoveredClass internal constructor( ".$ext" } - override val name: String by lazy { + override val name: String by lazy(LazyThreadSafetyMode.PUBLICATION) { annotationsFromAsm.find { it.name == KotlinScript::class.simpleName!! }?.args?.first() ?: super.name } diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptiDefinitionsFromClasspathDiscoverySource.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptiDefinitionsFromClasspathDiscoverySource.kt index acbf053d3ac..6de535af501 100644 --- a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptiDefinitionsFromClasspathDiscoverySource.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptiDefinitionsFromClasspathDiscoverySource.kt @@ -52,7 +52,7 @@ internal fun discoverScriptTemplatesInClasspath( messageCollector: MessageCollector ): Sequence = buildSequence { // TODO: try to find a way to reduce classpath (and classloader) to minimal one needed to load script definition and its dependencies - val classLoader by lazy { + val classLoader by lazy(LazyThreadSafetyMode.PUBLICATION) { URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), baseClassLoader) } for (dep in classpath) { @@ -89,10 +89,10 @@ internal fun discoverScriptTemplatesInClasspath( dep.isDirectory -> { val dir = File(dep, SCRIPT_DEFINITION_MARKERS_PATH) if (dir.isDirectory) { - val templateClasspath by lazy { + val templateClasspath by lazy(LazyThreadSafetyMode.PUBLICATION) { listOf(dep) + defaultScriptDefinitionClasspath } - val classLoader by lazy { + val classLoader by lazy(LazyThreadSafetyMode.PUBLICATION) { URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassLoader) } dir.listFiles().forEach { templateClassNmae -> @@ -154,10 +154,10 @@ internal fun loadScriptTemplatesFromClasspath( } // then searching the remaining templates in the supplied classpath if (templatesLeftToFind.isNotEmpty()) { - val templateClasspath by lazy { + val templateClasspath by lazy(LazyThreadSafetyMode.PUBLICATION) { classpath + dependenciesClasspath } - val classLoader by lazy { + val classLoader by lazy(LazyThreadSafetyMode.PUBLICATION) { URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassLoader) } for (dep in classpath) { diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/asmBasedAnnotationsLoading.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/asmBasedAnnotationsLoading.kt index 0c3672029df..485fcd73546 100644 --- a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/asmBasedAnnotationsLoading.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/asmBasedAnnotationsLoading.kt @@ -5,12 +5,7 @@ package org.jetbrains.kotlin.scripting.compiler.plugin -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.org.objectweb.asm.AnnotationVisitor -import org.jetbrains.org.objectweb.asm.ClassReader -import org.jetbrains.org.objectweb.asm.ClassVisitor -import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.* internal class BinAnnData( val name: String, @@ -25,7 +20,7 @@ private class TemplateAnnotationVisitor(val anns: ArrayList = arrayL private class TemplateClassVisitor(val annVisitor: TemplateAnnotationVisitor) : ClassVisitor(Opcodes.ASM5) { override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor { - val shortName = jvmDescToClassId(desc).shortClassName.asString() + val shortName = Type.getType(desc).internalName.substringAfterLast("/") if (shortName.startsWith("KotlinScript")) { annVisitor.anns.add(BinAnnData(shortName)) } @@ -33,13 +28,6 @@ private class TemplateClassVisitor(val annVisitor: TemplateAnnotationVisitor) : } } -private fun jvmDescToClassId(desc: String): ClassId { - assert(desc.startsWith("L") && desc.endsWith(";")) { "Not a JVM descriptor: $desc" } - val name = desc.substring(1, desc.length - 1) - val cid = ClassId.topLevel(FqName(name.replace('/', '.'))) - return cid -} - internal fun loadAnnotationsFromClass(fileContents: ByteArray): ArrayList { val visitor = diff --git a/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt b/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt index 00043317a45..d8dc6b809c0 100644 --- a/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt +++ b/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt @@ -36,7 +36,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() { const val TEST_DATA_DIR = "plugins/scripting/scripting-cli/testData" } - private val kotlinPaths: KotlinPaths by lazy { + private val kotlinPaths: KotlinPaths by lazy(LazyThreadSafetyMode.PUBLICATION) { val paths = PathUtil.kotlinPathsForDistDirectory TestCase.assertTrue("Lib directory doesn't exist. Run 'ant dist'", paths.libPath.absoluteFile.isDirectory) paths