Merge scripting-impl to scripting-compiler-impl module...

to continue merging with the branch with further scripting refactorings
This commit is contained in:
Ilya Chernikov
2019-04-11 16:58:28 +02:00
parent a82b386d81
commit 199b32cad7
30 changed files with 17 additions and 67 deletions
@@ -9,6 +9,7 @@ plugins {
dependencies {
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":compiler:frontend.script"))
compileOnly(project(":compiler:psi"))
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:cli"))
@@ -37,15 +38,15 @@ sourceSets {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions {
languageVersion = "1.3"
apiVersion = "1.3"
languageVersion = "1.2"
apiVersion = "1.2"
freeCompilerArgs += "-Xskip-metadata-version-check"
}
}
publish()
val jar = runtimeJar {}
runtimeJar()
sourcesJar()
javadocJar()
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.definitions
import com.intellij.openapi.fileTypes.LanguageFileType
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.starProjectedType
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.getScriptingClass
import kotlin.script.experimental.jvm.impl.BridgeDependenciesResolver
import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.experimental.util.getOrError
// temporary trick with passing Any as a template and overwriting it below, TODO: fix after introducing new script definitions hierarchy
abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinition(Any::class) {
abstract val scriptCompilationConfiguration: ScriptCompilationConfiguration
abstract val hostConfiguration: ScriptingHostConfiguration
open val baseClass: KClass<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
getScriptingClass(scriptCompilationConfiguration.getOrError(ScriptCompilationConfiguration.baseClass))
}
override val template: KClass<*> get() = baseClass
override val name: String
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.displayName] ?: "Kotlin Script"
override val fileType: LanguageFileType = KotlinFileType.INSTANCE
override fun isScript(fileName: String): Boolean =
fileName.endsWith(".$fileExtension")
override fun getScriptName(script: KtScript): Name {
val fileBasedName = NameUtils.getScriptNameForFile(script.containingKtFile.name)
return Name.identifier(fileBasedName.identifier.removeSuffix(".$fileExtension"))
}
override val annotationsForSamWithReceivers: List<String>
get() = emptyList()
override val dependencyResolver: DependenciesResolver by lazy(LazyThreadSafetyMode.PUBLICATION) {
BridgeDependenciesResolver(scriptCompilationConfiguration)
}
override val acceptedAnnotations: List<KClass<out Annotation>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scriptCompilationConfiguration[ScriptCompilationConfiguration.refineConfigurationOnAnnotations]?.annotations
.orEmpty()
.map { getScriptingClass(it) as KClass<out Annotation> }
}
override val implicitReceivers: List<KType> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scriptCompilationConfiguration[ScriptCompilationConfiguration.implicitReceivers]
.orEmpty()
.map { getScriptingClass(it).starProjectedType }
}
override val providedProperties: List<Pair<String, KType>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scriptCompilationConfiguration[ScriptCompilationConfiguration.providedProperties]
?.map { (k, v) -> k to getScriptingClass(v).starProjectedType }.orEmpty()
}
override val additionalCompilerArguments: List<String>
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]
.orEmpty()
override val scriptExpectedLocations: List<ScriptExpectedLocation>
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.ide.acceptedLocations]?.map {
when(it) {
ScriptAcceptedLocation.Sources -> ScriptExpectedLocation.SourcesOnly
ScriptAcceptedLocation.Tests -> ScriptExpectedLocation.TestsOnly
ScriptAcceptedLocation.Libraries -> ScriptExpectedLocation.Libraries
ScriptAcceptedLocation.Project -> ScriptExpectedLocation.Project
ScriptAcceptedLocation.Everywhere -> ScriptExpectedLocation.Everywhere
}
} ?: listOf(ScriptExpectedLocation.SourcesOnly, ScriptExpectedLocation.TestsOnly)
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
hostConfiguration[ScriptingHostConfiguration.getScriptingClass]
?: throw IllegalArgumentException("Expecting 'getScriptingClass' property in the scripting environment")
}
private fun getScriptingClass(type: KotlinType) =
scriptingClassGetter(
type,
KotlinScriptDefinition::class, // Assuming that the KotlinScriptDefinition class is loaded in the proper classloader
hostConfiguration
)
}
class KotlinScriptDefinitionAdapterFromNewAPI(
override val scriptCompilationConfiguration: ScriptCompilationConfiguration,
override val hostConfiguration: ScriptingHostConfiguration
) : KotlinScriptDefinitionAdapterFromNewAPIBase() {
override val name: String get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.displayName] ?: super.name
override val fileExtension: String
get() = scriptCompilationConfiguration[ScriptCompilationConfiguration.fileExtension] ?: super.fileExtension
}
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.definitions
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import java.io.File
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.fileExtension
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.JvmDependency
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class LazyScriptDefinitionFromDiscoveredClass internal constructor(
private val annotationsFromAsm: ArrayList<BinAnnData>,
private val className: String,
private val classpath: List<File>,
private val messageCollector: MessageCollector
) : KotlinScriptDefinitionAdapterFromNewAPIBase() {
constructor(
classBytes: ByteArray,
className: String,
classpath: List<File>,
messageCollector: MessageCollector
) : this(loadAnnotationsFromClass(classBytes), className, classpath, messageCollector)
override val hostConfiguration: ScriptingHostConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) {
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
configurationDependencies.append(JvmDependency(classpath))
}
}
override val scriptCompilationConfiguration: ScriptCompilationConfiguration by lazy(LazyThreadSafetyMode.PUBLICATION) {
messageCollector.report(
CompilerMessageSeverity.LOGGING,
"Configure scripting: loading script definition class $className using classpath $classpath\n. ${Thread.currentThread().stackTrace}"
)
try {
createCompilationConfigurationFromTemplate(
KotlinType(className),
hostConfiguration,
LazyScriptDefinitionFromDiscoveredClass::class
)
} catch (ex: ClassNotFoundException) {
messageCollector.report(CompilerMessageSeverity.ERROR, "Cannot find script definition class $className")
InvalidScriptDefinition
} catch (ex: Exception) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Error processing script definition class $className: ${ex.message}\nclasspath:\n${classpath.joinToString("\n", " ")}"
)
InvalidScriptDefinition
}
}
override val fileExtension: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
annotationsFromAsm.find { it.name == KotlinScript::class.simpleName }?.args
?.find { it.name == "fileExtension" }?.value
?: scriptCompilationConfiguration.let {
it[ScriptCompilationConfiguration.fileExtension] ?: super.fileExtension
}
}
override val name: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
annotationsFromAsm.find { it.name == KotlinScript::class.simpleName!! }?.args?.find { it.name == "name" }?.value
?: super.name
}
}
val InvalidScriptDefinition = ScriptCompilationConfiguration()
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.definitions
import com.intellij.ide.highlighter.JavaFileType
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
protected val lock = ReentrantReadWriteLock()
protected abstract val currentDefinitions: Sequence<KotlinScriptDefinition>
private var _cachedDefinitions: Sequence<KotlinScriptDefinition>? = null
private val cachedDefinitions: Sequence<KotlinScriptDefinition>
get() {
assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" }
if (_cachedDefinitions == null) lock.write {
_cachedDefinitions = CachingSequence(currentDefinitions.constrainOnce())
}
return _cachedDefinitions!!
}
protected fun clearCache() {
lock.write {
_cachedDefinitions = null
}
}
protected open fun nonScriptFileName(fileName: String) = nonScriptFilenameSuffixes.any {
fileName.endsWith(it, ignoreCase = true)
}
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? =
if (nonScriptFileName(fileName)) null
else lock.read {
cachedDefinitions.firstOrNull { it.isScript(fileName) }
}
override fun isScript(fileName: String) = findScriptDefinition(fileName) != null
override fun getKnownFilenameExtensions(): Sequence<String> = lock.read {
cachedDefinitions.map { it.fileExtension }
}
companion object {
// TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere
protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}")
}
}
private class CachingSequence<T>(from: Sequence<T>) : Sequence<T> {
private val lock = ReentrantReadWriteLock()
private val sequenceIterator = from.iterator()
private val cache = arrayListOf<T>()
private inner class CachingIterator : Iterator<T> {
private var cacheCursor = 0
override fun hasNext(): Boolean =
lock.read { cacheCursor < cache.size }
// iterator's hasNext can mutate the iterator's state, therefore write lock is needed
|| lock.write { cacheCursor < cache.size || sequenceIterator.hasNext() }
override fun next(): T {
lock.read {
if (cacheCursor < cache.size) return cache[cacheCursor++]
}
// lock.write is not an upgrade but retake, therefore - one more check needed
lock.write {
return if (cacheCursor < cache.size) cache[cacheCursor++]
else sequenceIterator.next().also { cache.add(it) }
}
}
}
override fun iterator(): Iterator<T> = CachingIterator()
}
@@ -0,0 +1,345 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.definitions
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.ScriptDefinitionsSource
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import java.io.IOException
import java.net.URLClassLoader
import java.util.jar.JarFile
import kotlin.coroutines.experimental.buildSequence
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.KotlinType
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.ScriptTemplateDefinition
const val SCRIPT_DEFINITION_MARKERS_PATH = "META-INF/kotlin/script/templates/"
const val SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT = ".classname"
class ScriptDefinitionsFromClasspathDiscoverySource(
private val classpath: List<File>,
private val scriptResolverEnv: Map<String, Any?>,
private val messageCollector: MessageCollector
) : ScriptDefinitionsSource {
override val definitions: Sequence<KotlinScriptDefinition> = run {
discoverScriptTemplatesInClasspath(
classpath,
this::class.java.classLoader,
scriptResolverEnv,
messageCollector
)
}
}
fun discoverScriptTemplatesInClasspath(
classpath: List<File>,
baseClassLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): Sequence<KotlinScriptDefinition> = buildSequence {
// TODO: try to find a way to reduce classpath (and classloader) to minimal one needed to load script definition and its dependencies
val loader = LazyClasspathWithClassLoader(baseClassLoader) { classpath }
// for jar files the definition class is expected in the same jar as the discovery file
// in case of directories, the class output may come separate from the resources, so some candidates should be deffered and processed later
val defferedDirDependencies = ArrayList<File>()
val defferedDefinitionCandidates = ArrayList<String>()
for (dep in classpath) {
try {
when {
dep.isFile && dep.extension == "jar" -> { // checking for extension is the compiler current behaviour, so the same logic is implemented here
JarFile(dep).use { jar ->
if (jar.getJarEntry(SCRIPT_DEFINITION_MARKERS_PATH) != null) {
val definitionNames = jar.entries().asSequence().mapNotNull {
if (it.isDirectory || !it.name.startsWith(SCRIPT_DEFINITION_MARKERS_PATH)) null
else it.name.removePrefix(SCRIPT_DEFINITION_MARKERS_PATH).removeSuffix(SCRIPT_DEFINITION_MARKERS_EXTENSION_WITH_DOT)
}.toList()
val (loadedDefinitions, notFoundClasses) =
definitionNames.partitionLoadJarDefinitions(jar, loader, scriptResolverEnv, messageCollector)
if (notFoundClasses.isNotEmpty()) {
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Configure scripting: unable to find script definitions [${notFoundClasses.joinToString(", ")}]"
)
}
loadedDefinitions.forEach {
yield(it)
}
}
}
}
dep.isDirectory -> {
defferedDirDependencies.add(dep) // there is no way to know that the dependency is fully "used" so we add it to the list anyway
val discoveryDir = File(dep, SCRIPT_DEFINITION_MARKERS_PATH)
if (discoveryDir.isDirectory) {
val (foundDefinitionClasses, notFoundDefinitions) = discoveryDir.listFiles().map { it.name }
.partitionLoadDirDefinitions(dep, loader, scriptResolverEnv, messageCollector)
foundDefinitionClasses.forEach {
yield(it)
}
defferedDefinitionCandidates.addAll(notFoundDefinitions)
}
}
else -> {
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
messageCollector.report(CompilerMessageSeverity.LOGGING, "Configure scripting: Unknown classpath entry $dep")
}
}
} catch (e: IOException) {
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING, "Configure scripting: unable to process classpath entry $dep: $e"
)
}
}
var remainingDefinitionCandidates: List<String> = defferedDefinitionCandidates
for (dep in defferedDirDependencies) {
if (remainingDefinitionCandidates.isEmpty()) break
try {
val (foundDefinitionClasses, notFoundDefinitions) =
remainingDefinitionCandidates.partitionLoadDirDefinitions(dep, loader, scriptResolverEnv, messageCollector)
foundDefinitionClasses.forEach {
yield(it)
}
remainingDefinitionCandidates = notFoundDefinitions
} catch (e: IOException) {
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING, "Configure scripting: unable to process classpath entry $dep: $e"
)
}
}
if (remainingDefinitionCandidates.isNotEmpty()) {
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING,
"The following script definitions are not found in the classpath: [${remainingDefinitionCandidates.joinToString()}]"
)
}
}
fun loadScriptTemplatesFromClasspath(
scriptTemplates: List<String>,
classpath: List<File>,
dependenciesClasspath: List<File>,
baseClassLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): Sequence<KotlinScriptDefinition> =
if (scriptTemplates.isEmpty()) emptySequence()
else buildSequence {
// trying the direct classloading from baseClassloader first, since this is the most performant variant
val (initialLoadedDefinitions, initialNotFoundTemplates) = scriptTemplates.partitionMapNotNull {
loadScriptDefinition(
baseClassLoader,
it,
scriptResolverEnv,
messageCollector
)
}
initialLoadedDefinitions.forEach {
yield(it)
}
// then searching the remaining templates in the supplied classpath
var remainingTemplates = initialNotFoundTemplates
val classpathAndLoader =
LazyClasspathWithClassLoader(baseClassLoader) { classpath + dependenciesClasspath }
for (dep in classpath) {
if (remainingTemplates.isEmpty()) break
try {
val (loadedDefinitions, notFoundTemplates) = when {
dep.isFile && dep.extension == "jar" -> { // checking for extension is the compiler current behaviour, so the same logic is implemented here
JarFile(dep).use { jar ->
remainingTemplates.partitionLoadJarDefinitions(jar, classpathAndLoader, scriptResolverEnv, messageCollector)
}
}
dep.isDirectory -> {
remainingTemplates.partitionLoadDirDefinitions(dep, classpathAndLoader, scriptResolverEnv, messageCollector)
}
else -> {
// assuming that invalid classpath entries will be reported elsewhere anyway, so do not spam user with additional warnings here
messageCollector.report(CompilerMessageSeverity.LOGGING, "Configure scripting: Unknown classpath entry $dep")
DefinitionsLoadPartitionResult(
listOf(),
remainingTemplates
)
}
}
if (loadedDefinitions.isNotEmpty()) {
loadedDefinitions.forEach {
yield(it)
}
remainingTemplates = notFoundTemplates
}
} catch (e: IOException) {
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Configure scripting: unable to process classpath entry $dep: $e"
)
}
}
if (remainingTemplates.isNotEmpty()) {
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Configure scripting: unable to find script definition classes: ${remainingTemplates.joinToString(", ")}"
)
}
}
private data class DefinitionsLoadPartitionResult(
val loaded: List<KotlinScriptDefinition>,
val notFound: List<String>
)
private inline fun List<String>.partitionLoadDefinitions(
classpathAndLoader: LazyClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector,
getBytes: (String) -> ByteArray?
): DefinitionsLoadPartitionResult {
val loaded = ArrayList<KotlinScriptDefinition>()
val notFound = ArrayList<String>()
for (definitionName in this) {
val classBytes = getBytes(definitionName)
val definition = classBytes?.let {
loadScriptDefinition(
it,
definitionName,
classpathAndLoader,
scriptResolverEnv,
messageCollector
)
}
when {
definition != null -> loaded.add(definition)
classBytes != null -> {}
else -> notFound.add(definitionName)
}
}
return DefinitionsLoadPartitionResult(loaded, notFound)
}
private fun List<String>.partitionLoadJarDefinitions(
jar: JarFile,
classpathAndLoader: LazyClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathAndLoader, scriptResolverEnv, messageCollector) { definitionName ->
jar.getJarEntry("${definitionName.replace('.', '/')}.class")?.let { jar.getInputStream(it).readBytes() }
}
private fun List<String>.partitionLoadDirDefinitions(
dir: File,
classpathAndLoader: LazyClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): DefinitionsLoadPartitionResult = partitionLoadDefinitions(classpathAndLoader, scriptResolverEnv, messageCollector) { definitionName ->
File(dir, "${definitionName.replace('.', '/')}.class").takeIf { it.exists() && it.isFile }?.readBytes()
}
private fun loadScriptDefinition(
templateClassBytes: ByteArray,
templateClassName: String,
classpathAndLoader: LazyClasspathWithClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): KotlinScriptDefinition? {
val anns = loadAnnotationsFromClass(templateClassBytes)
for (ann in anns) {
var def: KotlinScriptDefinition? = null
if (ann.name == KotlinScript::class.simpleName) {
def = LazyScriptDefinitionFromDiscoveredClass(
anns,
templateClassName,
classpathAndLoader.classpath,
messageCollector
)
} else if (ann.name == ScriptTemplateDefinition::class.simpleName) {
val templateClass = classpathAndLoader.classLoader.loadClass(templateClassName).kotlin
def = KotlinScriptDefinitionFromAnnotatedTemplate(templateClass, scriptResolverEnv, classpathAndLoader.classpath)
}
if (def != null) {
messageCollector.report(
CompilerMessageSeverity.LOGGING,
"Configure scripting: Added template $templateClassName from ${classpathAndLoader.classpath}"
)
return def
}
}
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Configure scripting: $templateClassName is not marked with any known kotlin script annotation"
)
return null
}
private fun loadScriptDefinition(
classLoader: ClassLoader,
template: String,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): KotlinScriptDefinition? {
try {
val cls = classLoader.loadClass(template)
val def =
if (cls.annotations.firstIsInstanceOrNull<KotlinScript>() != null) {
val environment = defaultJvmScriptingHostConfiguration
KotlinScriptDefinitionAdapterFromNewAPI(
createCompilationConfigurationFromTemplate(
KotlinType(cls.kotlin),
environment,
KotlinScriptDefinition::class
),
environment
)
} else {
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
}
messageCollector.report(
CompilerMessageSeverity.INFO,
"Added script definition $template to configuration: name = ${def.name}, " +
"resolver = ${def.dependencyResolver.javaClass.name}"
)
return def
} catch (ex: ClassNotFoundException) {
// not found - not an error, return null
} catch (ex: Exception) {
// other exceptions - might be an error
messageCollector.report(
CompilerMessageSeverity.STRONG_WARNING,
"Error on loading script definition $template: ${ex.message}"
)
}
return null
}
private class LazyClasspathWithClassLoader(baseClassLoader: ClassLoader, getClasspath: () -> List<File>) {
val classpath by lazy { getClasspath() }
val classLoader by lazy {
URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), baseClassLoader)
}
}
private inline fun <T, R> Iterable<T>.partitionMapNotNull(fn: (T) -> R?): Pair<List<R>, List<T>> {
val mapped = ArrayList<R>()
val failed = ArrayList<T>()
for (v in this) {
val r = fn(v)
if (r != null) {
mapped.add(r)
} else {
failed.add(v)
}
}
return mapped to failed
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.definitions
import org.jetbrains.org.objectweb.asm.*
internal class BinAnnArgData(
val name: String?,
val value: String
)
internal class BinAnnData(
val name: String,
val args: ArrayList<BinAnnArgData> = arrayListOf()
)
private class TemplateAnnotationVisitor(val anns: ArrayList<BinAnnData> = arrayListOf()) : AnnotationVisitor(Opcodes.API_VERSION) {
override fun visit(name: String?, value: Any?) {
anns.last().args.add(BinAnnArgData(name, value.toString()))
}
}
private class TemplateClassVisitor(val annVisitor: TemplateAnnotationVisitor) : ClassVisitor(Opcodes.API_VERSION) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
val shortName = Type.getType(desc).internalName.substringAfterLast("/")
if (shortName.startsWith("KotlinScript") || shortName.startsWith("ScriptTemplate")) {
annVisitor.anns.add(BinAnnData(shortName))
return annVisitor
}
return null
}
}
internal fun loadAnnotationsFromClass(fileContents: ByteArray): ArrayList<BinAnnData> {
val visitor =
TemplateClassVisitor(TemplateAnnotationVisitor())
ClassReader(fileContents).accept(visitor, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
return visitor.annVisitor.anns
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.definitions
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
fun PsiFile.scriptDefinition(): KotlinScriptDefinition? {
// Do not use psiFile.script, see comments in findScriptDefinition
if (this !is KtFile/* || this.script == null*/) return null
val file = virtualFile ?: originalFile.virtualFile ?: return null
if (file.isNonScript()) return null
return scriptDefinitionByFileName(project, file.name)
}
fun VirtualFile.findScriptDefinition(project: Project): KotlinScriptDefinition? {
if (isNonScript()) return null
// Do not use psiFile.script here because this method can be called during indexes access
// and accessing stubs may cause deadlock
// TODO: measure performance effect and if necessary consider detecting indexing here or using separate logic for non-IDE operations to speed up filtering
if ((PsiManager.getInstance(project).findFile(this) as? KtFile)/*?.script*/ == null) return null
return scriptDefinitionByFileName(project, name)
}
fun scriptDefinitionByFileName(project: Project, fileName: String): KotlinScriptDefinition {
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) ?: return null
?: throw IllegalStateException("Unable to get script definition: ScriptDefinitionProvider is not configured.")
return scriptDefinitionProvider.findScriptDefinition(fileName) ?: scriptDefinitionProvider.getDefaultScriptDefinition()
}
private fun VirtualFile.isNonScript(): Boolean =
isDirectory ||
extension == KotlinFileType.EXTENSION ||
extension == JavaClassFileType.INSTANCE.defaultExtension ||
!this.isKotlinFileType()
private fun VirtualFile.isKotlinFileType(): Boolean {
val typeRegistry = FileTypeRegistry.getInstance()
return typeRegistry.getFileTypeByFile(this) == KotlinFileType.INSTANCE ||
typeRegistry.getFileTypeByFileName(name) == KotlinFileType.INSTANCE
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.extensions
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtImportInfo
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
// initially copied from org.jetbrains.kotlin.resolve.lazy.FileScopeFactory.DefaultImportImpl, but kept separate to simplify dependencies
// and allow easier extension
private class ScriptExtraImportImpl(private val importPath: ImportPath) : KtImportInfo {
override val isAllUnder: Boolean get() = importPath.isAllUnder
override val importContent = KtImportInfo.ImportContent.FqNameBased(importPath.fqName)
override val aliasName: String? get() = importPath.alias?.asString()
override val importedFqName: FqName? get() = importPath.fqName
}
override fun getExtraImports(ktFile: KtFile): Collection<KtImportInfo> =
ktFile.takeIf { it.isScript() }?.let { file ->
val scriptDependencies = ScriptDependenciesProvider.getInstance(file.project)?.getScriptDependencies(file.originalFile)
scriptDependencies?.imports?.map {
ScriptExtraImportImpl(
ImportPath.fromString(it)
)
}
}.orEmpty()
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.extensions
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.PackageMemberDeclarationProvider
import org.jetbrains.kotlin.scripting.shared.resolve.LazyScriptDescriptor
class ScriptingResolveExtension : SyntheticResolveExtension {
override fun generateSyntheticClasses(
thisDescriptor: PackageFragmentDescriptor,
name: Name,
ctx: LazyClassContext,
declarationProvider: PackageMemberDeclarationProvider,
result: MutableSet<ClassDescriptor>
) {
declarationProvider.getScriptDeclarations(name).mapTo(result) {
LazyScriptDescriptor(ctx as ResolveSession, thisDescriptor, name, it)
}
super.generateSyntheticClasses(thisDescriptor, name, ctx, declarationProvider, result)
}
}
@@ -0,0 +1,8 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared
const val KOTLIN_SCRIPTING_PLUGIN_ID = "kotlin.scripting"
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassMemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
class LazyScriptClassMemberScope(
resolveSession: ResolveSession,
declarationProvider: ClassMemberDeclarationProvider,
private val scriptDescriptor: LazyScriptDescriptor,
trace: BindingTrace
) : LazyClassMemberScope(resolveSession, declarationProvider, scriptDescriptor, trace) {
private val scriptPrimaryConstructor: () -> ClassConstructorDescriptorImpl? = resolveSession.storageManager.createNullableLazyValue {
val baseClass = scriptDescriptor.baseClassDescriptor()
val baseConstructorDescriptor = baseClass?.unsubstitutedPrimaryConstructor
if (baseConstructorDescriptor != null) {
val implicitReceiversParamTypes =
scriptDescriptor.implicitReceivers.mapIndexed { idx, receiver ->
val name =
if (receiver is ScriptDescriptor) "$IMPORTED_SCRIPT_PARAM_NAME_PREFIX${receiver.name}"
else "$IMPLICIT_RECEIVER_PARAM_NAME_PREFIX$idx"
name to receiver.defaultType
}
val providedPropertiesParamTypes =
scriptDescriptor.scriptProvidedProperties.map {
it.name.identifier to it.type
}
val annotations = baseConstructorDescriptor.annotations
val constructorDescriptor = ClassConstructorDescriptorImpl.create(
scriptDescriptor, annotations, baseConstructorDescriptor.isPrimary, scriptDescriptor.source
)
var paramsIndexBase = baseConstructorDescriptor.valueParameters.lastIndex + 1
val syntheticParameters =
(implicitReceiversParamTypes + providedPropertiesParamTypes).map { param: Pair<String, KotlinType> ->
ValueParameterDescriptorImpl(
constructorDescriptor,
null,
paramsIndexBase++,
Annotations.EMPTY,
Name.identifier(param.first),
param.second,
false, false, false, null, SourceElement.NO_SOURCE
)
}
val parameters = baseConstructorDescriptor.valueParameters.map { it.copy(constructorDescriptor, it.name, it.index) } +
syntheticParameters
constructorDescriptor.initialize(parameters, baseConstructorDescriptor.visibility)
constructorDescriptor.returnType = scriptDescriptor.defaultType
constructorDescriptor
} else {
null
}
}
override fun resolvePrimaryConstructor(): ClassConstructorDescriptor? {
val constructor = scriptPrimaryConstructor()
?: ClassConstructorDescriptorImpl.create(
scriptDescriptor,
Annotations.EMPTY,
true,
SourceElement.NO_SOURCE
).initialize(
emptyList(),
Visibilities.PUBLIC
)
setDeferredReturnType(constructor)
return constructor
}
override fun getNonDeclaredProperties(name: Name, result: MutableSet<PropertyDescriptor>) {
super.getNonDeclaredProperties(name, result)
if (scriptDescriptor.resultFieldName() == name.asString()) {
scriptDescriptor.resultValue?.let {
result.add(it)
}
}
}
override fun createPropertiesFromPrimaryConstructorParameters(name: Name, result: MutableSet<PropertyDescriptor>) {
}
companion object {
const val IMPLICIT_RECEIVER_PARAM_NAME_PREFIX = "\$\$implicitReceiver"
const val IMPORTED_SCRIPT_PARAM_NAME_PREFIX = "\$\$importedScript"
}
}
private fun ClassDescriptor.substitute(vararg types: KotlinType): KotlinType? =
KotlinTypeFactory.simpleType(this.defaultType, arguments = types.map { it.asTypeProjection() })
@@ -0,0 +1,254 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.resolve
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.MISSING_IMPORTED_SCRIPT_FILE
import org.jetbrains.kotlin.diagnostics.Errors.MISSING_IMPORTED_SCRIPT_PSI
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.source.toSourceElement
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.scripting.shared.definitions.scriptDefinitionByFileName
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KType
class LazyScriptDescriptor(
val resolveSession: ResolveSession,
containingDeclaration: DeclarationDescriptor,
name: Name,
internal val scriptInfo: KtScriptInfo
) : ScriptDescriptor, LazyClassDescriptor(
resolveSession,
containingDeclaration,
name,
scriptInfo,
/* isExternal = */ false
) {
init {
resolveSession.trace.record(BindingContext.SCRIPT, scriptInfo.script, this)
}
override fun getResultValue(): ReplResultPropertyDescriptor? {
val expression = scriptInfo.script
.getChildOfType<KtBlockExpression>()
?.getChildrenOfType<KtScriptInitializer>()?.lastOrNull()
?.getChildOfType<KtExpression>()
val type = expression?.let {
resolveSession.trace.bindingContext.getType(it)
}
return if (type != null && !type.isUnit() && !type.isNothing()) {
resultFieldName()?.let {
ReplResultPropertyDescriptor(
Name.identifier(it),
type,
this.thisAsReceiverParameter,
this,
expression.toSourceElement()
)
}
} else null
}
fun resultFieldName(): String? {
val scriptPriority = scriptInfo.script.getUserData(ScriptPriorities.PRIORITY_KEY)
if (scriptPriority != null) {
return "res$scriptPriority"
}
val scriptName = name.asString()
return if (scriptName.startsWith("Line_")) {
"res${scriptName.split("_")[1]}"
} else "\$\$result"
}
private val sourceElement = scriptInfo.script.toSourceElement()
override fun getSource() = sourceElement
private val priority: Int = ScriptPriorities.getScriptPriority(scriptInfo.script)
override fun getPriority() = priority
val scriptDefinition: () -> KotlinScriptDefinition = resolveSession.storageManager.createLazyValue {
scriptDefinitionByFileName(resolveSession.project, scriptInfo.script.containingKtFile.name)
}
override fun substitute(substitutor: TypeSubstitutor) = this
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitScriptDescriptor(this, data)
override fun createMemberScope(c: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider): LazyScriptClassMemberScope =
LazyScriptClassMemberScope(
// Must be a ResolveSession for scripts
c as ResolveSession,
declarationProvider,
this,
c.trace
)
override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!!
internal val baseClassDescriptor: () -> ClassDescriptor? = resolveSession.storageManager.createNullableLazyValue {
val template = scriptDefinition().template
findTypeDescriptor(
template,
if (template.qualifiedName?.startsWith("kotlin.script.templates.standard") == true) Errors.MISSING_SCRIPT_STANDARD_TEMPLATE
else Errors.MISSING_SCRIPT_BASE_CLASS
)
}
override fun computeSupertypes() = listOf(baseClassDescriptor()?.defaultType ?: builtIns.anyType)
private inner class ImportedScriptDescriptorsFinder {
val fileManager = VirtualFileManager.getInstance()
val localFS = fileManager.getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val psiManager = PsiManager.getInstance(scriptInfo.script.project)
operator fun invoke(importedScriptFile: File): ScriptDescriptor? {
fun errorDescriptor(errorDiagnostic: DiagnosticFactory1<PsiElement, String>?): ScriptDescriptor? {
reportErrorString1(errorDiagnostic, importedScriptFile.path)
return null
}
val vfile = localFS.findFileByPath(importedScriptFile.path)
?: return errorDescriptor(MISSING_IMPORTED_SCRIPT_FILE)
val psiFile = psiManager.findFile(vfile)
?: return errorDescriptor(MISSING_IMPORTED_SCRIPT_PSI)
// Note: is not an error now - if import references other valid source file, it is simply compiled along with script
// TODO: check if this is the behavior we want to have - see #KT-28916
val ktScript = (psiFile as? KtFile)?.declarations?.firstIsInstanceOrNull<KtScript>()
?: return null
return resolveSession.getScriptDescriptor(ktScript) as ScriptDescriptor
}
}
private val scriptImplicitReceivers: () -> List<ClassDescriptor> = resolveSession.storageManager.createLazyValue {
val res = ArrayList<ClassDescriptor>()
val importedScriptsFiles = ScriptDependenciesProvider.getInstance(scriptInfo.script.project)
?.getScriptDependencies(scriptInfo.script.containingKtFile)?.scripts
if (importedScriptsFiles != null) {
val findImportedScriptDescriptor = ImportedScriptDescriptorsFinder()
importedScriptsFiles.mapNotNullTo(res) {
findImportedScriptDescriptor(it)
}
}
scriptDefinition().implicitReceivers.mapNotNullTo(res) { receiver ->
findTypeDescriptor(receiver, Errors.MISSING_SCRIPT_RECEIVER_CLASS)
}
res
}
internal fun findTypeDescriptor(kClass: KClass<*>, errorDiagnostic: DiagnosticFactory1<PsiElement, String>?): ClassDescriptor? =
findTypeDescriptor(kClass.classId, kClass.toString(), errorDiagnostic)
internal fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1<PsiElement, String>?): ClassDescriptor? =
findTypeDescriptor(type.classId, type.toString(), errorDiagnostic)
internal fun findTypeDescriptor(
classId: ClassId?, typeName: String,
errorDiagnostic: DiagnosticFactory1<PsiElement, String>?
): ClassDescriptor? {
val typeDescriptor = classId?.let { module.findClassAcrossModuleDependencies(it) }
if (typeDescriptor == null) {
reportErrorString1(errorDiagnostic, classId?.asSingleFqName()?.toString() ?: typeName)
}
return typeDescriptor
}
private fun reportErrorString1(errorDiagnostic: DiagnosticFactory1<PsiElement, String>?, arg: String) {
if (errorDiagnostic != null) {
// TODO: use PositioningStrategies to highlight some specific place in case of error, instead of treating the whole file as invalid
resolveSession.trace.report(
errorDiagnostic.on(
scriptInfo.script,
arg
)
)
}
}
override fun getImplicitReceivers(): List<ClassDescriptor> = scriptImplicitReceivers()
private val scriptProvidedProperties: () -> ScriptProvidedPropertiesDescriptor = resolveSession.storageManager.createLazyValue {
ScriptProvidedPropertiesDescriptor(this)
}
override fun getScriptProvidedProperties(): List<PropertyDescriptor> = scriptProvidedProperties().properties()
private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue {
var outerScope = super.getOuterScope()
val outerScopeReceivers = implicitReceivers.let {
if (scriptDefinition().providedProperties.isEmpty()) {
it
} else {
it + ScriptProvidedPropertiesDescriptor(this)
}
}
for (receiverClassDescriptor in outerScopeReceivers.asReversed()) {
outerScope = LexicalScopeImpl(
outerScope,
receiverClassDescriptor,
true,
receiverClassDescriptor.thisAsReceiverParameter,
LexicalScopeKind.CLASS_MEMBER_SCOPE
)
}
outerScope
}
override fun getOuterScope(): LexicalScope = scriptOuterScope()
private val scriptClassAnnotations: () -> Annotations = resolveSession.storageManager.createLazyValue {
baseClassDescriptor()?.annotations?.let { ann ->
FilteredAnnotations(ann) { fqname ->
val shortName = fqname.shortName().identifier
// TODO: consider more precise annotation filtering
!shortName.startsWith("KotlinScript") && !shortName.startsWith("ScriptTemplate")
}
} ?: super.annotations
}
override val annotations: Annotations
get() = scriptClassAnnotations()
}
@@ -0,0 +1,39 @@
/*
* 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 org.jetbrains.kotlin.scripting.shared.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
class ReplResultPropertyDescriptor(
name: Name,
kotlinType: KotlinType,
receiver: ReceiverParameterDescriptor?,
script: ScriptDescriptor,
source: SourceElement
) : PropertyDescriptorImpl(
script,
null,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PUBLIC,
false,
name,
CallableMemberDescriptor.Kind.SYNTHESIZED,
source,
/* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false,
/* isDelegated = */ false
) {
init {
setType(kotlinType, emptyList(), receiver, null)
initialize(
null, null
)
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2019 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 org.jetbrains.kotlin.scripting.shared.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.Printer
class ScriptProvidedPropertiesDescriptor(script: LazyScriptDescriptor) :
MutableClassDescriptor(
script,
ClassKind.CLASS, false, false,
Name.special("<synthetic script provided properties for ${script.name}>"),
SourceElement.NO_SOURCE,
LockBasedStorageManager.NO_LOCKS
) {
init {
modality = Modality.FINAL
visibility = Visibilities.PUBLIC
setTypeParameterDescriptors(emptyList())
createTypeConstructor()
}
private val memberScope: () -> ScriptProvidedPropertiesMemberScope = script.resolveSession.storageManager.createLazyValue {
ScriptProvidedPropertiesMemberScope(
script.name.identifier,
properties()
)
}
override fun getUnsubstitutedMemberScope(): MemberScope = memberScope()
val properties: () -> List<ScriptProvidedPropertyDescriptor> = script.resolveSession.storageManager.createLazyValue {
script.scriptDefinition().providedProperties.mapNotNull { (name, type) ->
script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_PROVIDED_PROPERTY_CLASS)?.let {
name to it
}
}.map { (name, classDescriptor) ->
ScriptProvidedPropertyDescriptor(
Name.identifier(name),
classDescriptor,
thisAsReceiverParameter,
true,
script
)
}
}
private class ScriptProvidedPropertiesMemberScope(
private val scriptId: String,
private val providedProperties: List<PropertyDescriptor>
) : MemberScopeImpl() {
override fun getContributedDescriptors(
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> =
providedProperties
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> =
providedProperties.filter { it.name == name }
override fun printScopeStructure(p: Printer) {
p.println("Scope of script provided properties: $scriptId")
}
}
}
@@ -0,0 +1,91 @@
/*
* 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 org.jetbrains.kotlin.scripting.shared.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.name.Name
class ScriptProvidedPropertyDescriptor(
name: Name,
typeDescriptor: ClassDescriptor,
receiver: ReceiverParameterDescriptor?,
isVar: Boolean,
script: ScriptDescriptor
) : PropertyDescriptorImpl(
script,
null,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PRIVATE,
isVar,
name,
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE,
/* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false,
/* isDelegated = */ false
) {
init {
setType(typeDescriptor.defaultType, emptyList(), receiver, null)
initialize(
makePropertyGetterDescriptor(),
if (!isVar) null else makePropertySetterDescriptor()
)
}
}
private fun PropertyDescriptorImpl.makePropertyGetterDescriptor() =
PropertyGetterDescriptorImpl(
this,
Annotations.EMPTY,
this.modality,
this.visibility,
/* isDefault = */
false, /* isExternal = */
false, /* isInline = */
false,
this.kind,
null,
SourceElement.NO_SOURCE
).also {
it.initialize(returnType)
}
private fun PropertyDescriptorImpl.makePropertySetterDescriptor() =
PropertySetterDescriptorImpl(
this,
Annotations.EMPTY,
this.modality,
this.visibility,
/* isDefault = */
false, /* isExternal = */
false, /* isInline = */
false,
this.kind,
null,
SourceElement.NO_SOURCE
).also {
it.initialize(
ValueParameterDescriptorImpl(
this,
null,
0,
Annotations.EMPTY,
Name.special("<set-?>"),
returnType,
/* declaresDefaultValue = */
false, /* isCrossinline = */
false, /* isNoinline = */
false,
null,
SourceElement.NO_SOURCE
)
)
}
@@ -0,0 +1,19 @@
/*
* 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 org.jetbrains.kotlin.scripting.shared.resolve
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KClass
import kotlin.reflect.KType
val KClass<*>.classId: ClassId
get() = this.java.enclosingClass?.kotlin?.classId?.createNestedClassId(Name.identifier(simpleName!!))
?: ClassId.topLevel(FqName(qualifiedName!!))
val KType.classId: ClassId?
get() = classifier?.let { it as? KClass<*> }?.classId