Move scripting support classes to the scripting compiler impl module

This commit is contained in:
Ilya Chernikov
2019-02-26 15:38:01 +01:00
parent 8df829a7cd
commit a65dafc37d
95 changed files with 502 additions and 436 deletions
@@ -0,0 +1,34 @@
/*
* 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.configuration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import java.io.File
object ScriptingConfigurationKeys {
val SCRIPT_DEFINITIONS = CompilerConfigurationKey.create<List<KotlinScriptDefinition>>("script definitions")
val SCRIPT_DEFINITIONS_SOURCES =
CompilerConfigurationKey.create<List<ScriptDefinitionsSource>>("script definitions sources")
val DISABLE_SCRIPTING_PLUGIN_OPTION: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create("Disable scripting plugin")
val SCRIPT_DEFINITIONS_CLASSES: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create("Script definition classes")
val SCRIPT_DEFINITIONS_CLASSPATH: CompilerConfigurationKey<List<File>> =
CompilerConfigurationKey.create("Additional classpath for the script definitions")
val DISABLE_SCRIPT_DEFINITIONS_FROM_CLASSPATH_OPTION: CompilerConfigurationKey<Boolean> =
CompilerConfigurationKey.create("Do not extract script definitions from the compilation classpath")
val LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION: CompilerConfigurationKey<MutableMap<String, Any?>> =
CompilerConfigurationKey.create("Script resolver environment")
}
@@ -0,0 +1,28 @@
/*
* 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.configuration
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath
const val KOTLIN_SCRIPTING_PLUGIN_ID = "kotlin.scripting"
fun configureScriptDefinitions(
scriptTemplates: List<String>,
configuration: CompilerConfiguration,
baseClassloader: ClassLoader,
messageCollector: MessageCollector,
scriptResolverEnv: Map<String, Any?>
) {
// TODO: consider using escaping to allow kotlin escaped names in class names
val templatesFromClasspath = loadScriptTemplatesFromClasspath(
scriptTemplates, configuration.jvmClasspathRoots, emptyList(), baseClassloader, scriptResolverEnv, messageCollector
)
configuration.addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, templatesFromClasspath.toList())
}
@@ -0,0 +1,64 @@
/*
* 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.definitions
import com.intellij.openapi.fileTypes.LanguageFileType
import com.intellij.openapi.util.UserDataHolderBase
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtScript
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.templates.standard.ScriptTemplateWithArgs
open class KotlinScriptDefinition(open val template: KClass<out Any>) : UserDataHolderBase() {
open val name: String = "Kotlin Script"
// TODO: consider creating separate type (subtype? for kotlin scripts)
open val fileType: LanguageFileType = KotlinFileType.INSTANCE
open val annotationsForSamWithReceivers: List<String>
get() = emptyList()
open fun isScript(fileName: String): Boolean =
fileName.endsWith(KotlinParserDefinition.STD_SCRIPT_EXT)
open fun getScriptName(script: KtScript): Name =
NameUtils.getScriptNameForFile(script.containingKtFile.name)
open val fileExtension: String
get() = "kts"
// Target platform for script, ex. "JVM", "JS", "NATIVE"
open val platform: String
get() = "JVM"
open val dependencyResolver: DependenciesResolver get() = DependenciesResolver.NoDependencies
open val acceptedAnnotations: List<KClass<out Annotation>> get() = emptyList()
@Deprecated("temporary workaround for missing functionality, will be replaced by the new API soon")
open val additionalCompilerArguments: Iterable<String>? = null
open val scriptExpectedLocations: List<ScriptExpectedLocation> =
listOf(ScriptExpectedLocation.SourcesOnly, ScriptExpectedLocation.TestsOnly)
open val implicitReceivers: List<KType> get() = emptyList()
open val providedProperties: List<Pair<String, KType>> get() = emptyList()
open val targetClassAnnotations: List<Annotation> get() = emptyList()
open val targetMethodAnnotations: List<Annotation> get() = emptyList()
}
object StandardScriptDefinition : KotlinScriptDefinition(ScriptTemplateWithArgs::class)
@@ -10,7 +10,6 @@ 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
@@ -0,0 +1,24 @@
/*
* 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.definitions
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
interface ScriptDefinitionProvider {
fun findScriptDefinition(fileName: String): KotlinScriptDefinition?
fun isScript(fileName: String): Boolean
fun getDefaultScriptDefinition(): KotlinScriptDefinition
fun getKnownFilenameExtensions(): Sequence<String>
companion object {
fun getInstance(project: Project): ScriptDefinitionProvider? =
ServiceManager.getService(project, ScriptDefinitionProvider::class.java)
}
}
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.scripting.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
@@ -0,0 +1,11 @@
/*
* 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.definitions
interface ScriptDefinitionsSource {
val definitions: Sequence<KotlinScriptDefinition>
}
@@ -0,0 +1,22 @@
/*
* 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.definitions
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import kotlin.script.experimental.dependencies.ScriptDependencies
interface ScriptDependenciesProvider {
fun getScriptDependencies(file: VirtualFile): ScriptDependencies?
fun getScriptDependencies(file: PsiFile) = getScriptDependencies(file.virtualFile ?: file.originalFile.virtualFile)
companion object {
fun getInstance(project: Project): ScriptDependenciesProvider? =
ServiceManager.getService(project, ScriptDependenciesProvider::class.java)
}
}
@@ -0,0 +1,20 @@
/*
* 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.definitions;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.psi.KtScript;
public class ScriptPriorities {
public static final Key<Integer> PRIORITY_KEY = Key.create(KtScript.class.getName() + ".priority");
public static int getScriptPriority(@NotNull KtScript script) {
Integer priority = script.getUserData(PRIORITY_KEY);
return priority == null ? 0 : priority;
}
}
@@ -7,15 +7,12 @@ package org.jetbrains.kotlin.scripting.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.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
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
@@ -46,7 +43,7 @@ fun discoverScriptTemplatesInClasspath(
baseClassLoader: ClassLoader,
scriptResolverEnv: Map<String, Any?>,
messageCollector: MessageCollector
): Sequence<KotlinScriptDefinition> = buildSequence {
): Sequence<KotlinScriptDefinition> = sequence {
// 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 }
@@ -136,7 +133,7 @@ fun loadScriptTemplatesFromClasspath(
messageCollector: MessageCollector
): Sequence<KotlinScriptDefinition> =
if (scriptTemplates.isEmpty()) emptySequence()
else buildSequence {
else sequence {
// trying the direct classloading from baseClassloader first, since this is the most performant variant
val (initialLoadedDefinitions, initialNotFoundTemplates) = scriptTemplates.partitionMapNotNull {
loadScriptDefinition(
@@ -13,8 +13,6 @@ 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
@@ -3,16 +3,15 @@
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.compiler.plugin.dependencies
package org.jetbrains.kotlin.scripting.dependencies
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.createSourceFilesFromSourceRoots
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import java.io.File
data class ScriptsCompilationDependencies(
@@ -3,16 +3,16 @@
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.compiler.plugin
package org.jetbrains.kotlin.scripting.extensions
import com.intellij.core.JavaCoreProjectEnvironment
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.extensions.ReplFactoryExtension
import org.jetbrains.kotlin.cli.common.repl.ReplCompiler
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.repl.GenericReplCompiler
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import java.io.File
import java.net.URLClassLoader
@@ -10,7 +10,7 @@ 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
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
@@ -3,15 +3,15 @@
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.compiler.plugin
package org.jetbrains.kotlin.scripting.extensions
import com.intellij.mock.MockProject
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.scripting.compiler.plugin.dependencies.collectScriptsCompilationDependencies
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.extensions.CollectAdditionalSourcesExtension
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.dependencies.collectScriptsCompilationDependencies
class ScriptingCollectAdditionalSourcesExtension(val project: MockProject) : CollectAdditionalSourcesExtension {
override fun collectAdditionalSourcesAndUpdateConfiguration(
@@ -23,7 +23,8 @@ import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.repl.messages.ConsoleDiagnosticMessageHolder
import kotlin.concurrent.write
@@ -38,7 +39,7 @@ open class GenericReplChecker(
internal val environment = run {
compilerConfiguration.apply {
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
add(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
put<MessageCollector>(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
@@ -18,8 +18,8 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.*
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
private val topDownAnalysisContext: TopDownAnalysisContext
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.repl.configuration.ReplConfiguration
import java.io.PrintWriter
import java.net.URLClassLoader
@@ -0,0 +1,29 @@
/*
* 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.resolve
import kotlinx.coroutines.runBlocking
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
// wraps AsyncDependenciesResolver to provide implementation for synchronous DependenciesResolver::resolve
class AsyncDependencyResolverWrapper(
override val delegate: AsyncDependenciesResolver
): AsyncDependenciesResolver, DependencyResolverWrapper<AsyncDependenciesResolver> {
override fun resolve(
scriptContents: ScriptContents, environment: Environment
): DependenciesResolver.ResolveResult
= runBlocking { delegate.resolveAsync(scriptContents, environment) }
suspend override fun resolveAsync(
scriptContents: ScriptContents, environment: Environment
): DependenciesResolver.ResolveResult
= delegate.resolveAsync(scriptContents, environment)
}
@@ -0,0 +1,170 @@
/*
* 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.
*/
@file:Suppress("DEPRECATION")
package org.jetbrains.kotlin.scripting.resolve
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.primaryConstructor
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.location.ScriptExpectedLocation
import kotlin.script.experimental.location.ScriptExpectedLocations
import kotlin.script.templates.AcceptedAnnotations
import kotlin.script.templates.DEFAULT_SCRIPT_FILE_PATTERN
import kotlin.script.templates.ScriptTemplateDefinition
open class KotlinScriptDefinitionFromAnnotatedTemplate(
template: KClass<out Any>,
val environment: Map<String, Any?>? = null,
val templateClasspath: List<File> = emptyList()
) : KotlinScriptDefinition(template) {
val scriptFilePattern by lazy(LazyThreadSafetyMode.PUBLICATION) {
val pattern =
takeUnlessError {
val ann = template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>()
ann?.scriptFilePattern
}
?: takeUnlessError { template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>()?.scriptFilePattern }
?: DEFAULT_SCRIPT_FILE_PATTERN
Regex(pattern)
}
override val dependencyResolver: DependenciesResolver by lazy(LazyThreadSafetyMode.PUBLICATION) {
resolverFromAnnotation(template) ?:
DependenciesResolver.NoDependencies
}
private fun resolverFromAnnotation(template: KClass<out Any>): DependenciesResolver? {
val defAnn = takeUnlessError {
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>()
} ?: return null
val resolver = instantiateResolver(defAnn.resolver)
return when (resolver) {
is AsyncDependenciesResolver -> AsyncDependencyResolverWrapper(resolver)
is DependenciesResolver -> resolver
else -> resolver?.let(::ApiChangeDependencyResolverWrapper)
}
}
private fun <T : Any> instantiateResolver(resolverClass: KClass<T>): T? {
try {
resolverClass.objectInstance?.let {
return it
}
val constructorWithoutParameters = resolverClass.constructors.find { it.parameters.all { it.isOptional } }
if (constructorWithoutParameters == null) {
log.warn("[kts] ${resolverClass.qualifiedName} must have a constructor without required parameters")
return null
}
return constructorWithoutParameters.callBy(emptyMap())
}
catch (ex: ClassCastException) {
log.warn("[kts] Script def error ${ex.message}")
return null
}
}
private val samWithReceiverAnnotations: List<String>? by lazy(LazyThreadSafetyMode.PUBLICATION) {
takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.extensions.SamWithReceiverAnnotations>()?.annotations?.toList() }
}
override val acceptedAnnotations: List<KClass<out Annotation>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean =
left.name == right.name &&
left.parameters.size == right.parameters.size &&
left.parameters.zip(right.parameters).all {
it.first.kind == KParameter.Kind.INSTANCE ||
it.first.type == it.second.type
}
val resolveFunctions = getResolveFunctions()
dependencyResolver.unwrap()::class.memberFunctions
.filter { function -> resolveFunctions.any { sameSignature(function, it) } }
.flatMap { it.annotations }
.filterIsInstance<AcceptedAnnotations>()
.flatMap { it.supportedAnnotationClasses.toList() }
.distinctBy { it.qualifiedName }
}
private fun getResolveFunctions(): List<KFunction<*>> {
// DependenciesResolver::resolve, ScriptDependenciesResolver::resolve, AsyncDependenciesResolver::resolveAsync
return AsyncDependenciesResolver::class.memberFunctions.filter { it.name == "resolve" || it.name == "resolveAsync" }.also {
assert(it.size == 3) {
AsyncDependenciesResolver::class.memberFunctions
.joinToString(prefix = "${AsyncDependenciesResolver::class.qualifiedName} api changed, fix this code") { it.name }
}
}
}
override val scriptExpectedLocations: List<ScriptExpectedLocation> by lazy(LazyThreadSafetyMode.PUBLICATION) {
takeUnlessError {
template.annotations.firstIsInstanceOrNull<ScriptExpectedLocations>()
}?.value?.toList() ?: super.scriptExpectedLocations
}
override val name = template.simpleName!!
override fun isScript(fileName: String): Boolean =
scriptFilePattern.matches(fileName)
// TODO: implement other strategy - e.g. try to extract something from match with ScriptFilePattern
override fun getScriptName(script: KtScript): Name = NameUtils.getScriptNameForFile(script.containingKtFile.name)
override fun toString(): String = "KotlinScriptDefinitionFromAnnotatedTemplate - ${template.simpleName}"
override val annotationsForSamWithReceivers: List<String>
get() = samWithReceiverAnnotations ?: super.annotationsForSamWithReceivers
override val additionalCompilerArguments: Iterable<String>? by lazy(LazyThreadSafetyMode.PUBLICATION) {
takeUnlessError {
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateAdditionalCompilerArguments>()?.let {
val res = it.provider.primaryConstructor?.call(it.arguments.asIterable())
res
}
}?.getAdditionalCompilerArguments(environment)
}
private inline fun<T> takeUnlessError(reportError: Boolean = true, body: () -> T?): T? =
try {
body()
}
catch (ex: Throwable) {
if (reportError) {
log.error("Invalid script template: " + template.qualifiedName, ex)
}
else {
log.warn("Invalid script template: " + template.qualifiedName, ex)
}
null
}
companion object {
internal val log = Logger.getInstance(KotlinScriptDefinitionFromAnnotatedTemplate::class.java)
}
}
interface DependencyResolverWrapper<T : ScriptDependenciesResolver> {
val delegate: T
}
fun ScriptDependenciesResolver.unwrap(): ScriptDependenciesResolver {
return if (this is DependencyResolverWrapper<*>) delegate.unwrap() else this
}
@@ -33,9 +33,9 @@ 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.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptPriorities
import org.jetbrains.kotlin.scripting.definitions.scriptDefinitionByFileName
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isNothing
@@ -0,0 +1,96 @@
/*
* 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.resolve
import com.intellij.openapi.application.ApplicationManager
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.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import java.io.File
import kotlin.reflect.KClass
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult.Failure
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.ScriptReport
class ScriptContentLoader(private val project: Project) {
fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: VirtualFile)
= BasicScriptContents(
file,
getAnnotations = { loadAnnotations(scriptDefinition, file) })
private fun loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: VirtualFile): List<Annotation> {
val classLoader = scriptDefinition.template.java.classLoader
// TODO_R: report error on failure to load annotation class
return ApplicationManager.getApplication().runReadAction<List<Annotation>> {
getAnnotationEntries(file, project)
.mapNotNull { psiAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving
scriptDefinition.acceptedAnnotations.find { ann ->
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
}?.let {
constructAnnotation(
psiAnn,
classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>,
project
)
}
}
}
}
private fun getAnnotationEntries(file: VirtualFile, project: Project): Iterable<KtAnnotationEntry> {
val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file)
?: throw IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
return (psiFile as? KtFile)?.annotationEntries
?: throw IllegalArgumentException("Unable to extract kotlin annotations from ${file.name} (${file.fileType})")
}
class BasicScriptContents(virtualFile: VirtualFile, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
override val file: File = File(virtualFile.path)
override val annotations: Iterable<Annotation> by lazy(LazyThreadSafetyMode.PUBLICATION) { getAnnotations() }
override val text: CharSequence? by lazy(LazyThreadSafetyMode.PUBLICATION) { virtualFile.inputStream.reader(charset = virtualFile.charset).readText() }
}
fun loadContentsAndResolveDependencies(
scriptDef: KotlinScriptDefinition,
file: VirtualFile
): DependenciesResolver.ResolveResult {
val scriptContents = getScriptContents(scriptDef, file)
val environment = getEnvironment(scriptDef)
val result = try {
scriptDef.dependencyResolver.resolve(
scriptContents,
environment
)
}
catch (e: Throwable) {
e.asResolveFailure(scriptDef)
}
return result
}
fun getEnvironment(scriptDef: KotlinScriptDefinition) =
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
}
fun ScriptDependencies.adjustByDefinition(
scriptDef: KotlinScriptDefinition
): ScriptDependencies {
val additionalClasspath = (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.templateClasspath ?: return this
if (additionalClasspath.isEmpty()) return this
return copy(classpath = additionalClasspath + classpath)
}
fun Throwable.asResolveFailure(scriptDef: KotlinScriptDefinition): Failure {
val prefix = "${scriptDef.dependencyResolver::class.simpleName} threw exception ${this::class.simpleName}:\n "
return Failure(ScriptReport(prefix + (message ?: "<no message>"), ScriptReport.Severity.FATAL))
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.scripting.resolve
import com.intellij.openapi.vfs.VirtualFile
import kotlin.script.experimental.dependencies.ScriptReport
interface ScriptReportSink {
fun attachReports(scriptFile: VirtualFile, reports: List<ScriptReport>)
}
@@ -7,7 +7,6 @@
package org.jetbrains.kotlin.scripting.resolve
import org.jetbrains.kotlin.script.DependencyResolverWrapper
import java.io.File
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptDependenciesResolver
@@ -0,0 +1,58 @@
/*
* 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.resolve
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.tryCreateCallableMappingFromNamedArgs
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.primaryConstructor
internal val KtAnnotationEntry.typeName: String get() = (typeReference?.typeElement as? KtUserType)?.referencedName.orAnonymous()
internal fun String?.orAnonymous(kind: String = ""): String =
this ?: "<anonymous" + (if (kind.isNotBlank()) " $kind" else "") + ">"
internal fun constructAnnotation(psi: KtAnnotationEntry, targetClass: KClass<out Annotation>, project: Project): Annotation {
val module = ModuleDescriptorImpl(Name.special("<script-annotations-preprocessing>"), LockBasedStorageManager("scriptAnnotationsPreprocessing"), DefaultBuiltIns.Instance)
val evaluator = ConstantExpressionEvaluator(module, LanguageVersionSettingsImpl.DEFAULT, project)
val trace = BindingTraceContext()
val valueArguments = psi.valueArguments.map { arg ->
val result = evaluator.evaluateToConstantValue(arg.getArgumentExpression()!!, trace, TypeUtils.NO_EXPECTED_TYPE)
// TODO: consider inspecting `trace` to find diagnostics reported during the computation (such as division by zero, integer overflow, invalid annotation parameters etc.)
val argName = arg.getArgumentName()?.asName?.toString()
argName to result?.value
}
val mappedArguments: Map<KParameter, Any?> =
tryCreateCallableMappingFromNamedArgs(targetClass.constructors.first(), valueArguments)
?: return InvalidScriptResolverAnnotation(psi.typeName, valueArguments)
try {
return targetClass.primaryConstructor!!.callBy(mappedArguments)
}
catch (ex: Exception) {
return InvalidScriptResolverAnnotation(psi.typeName, valueArguments, ex)
}
}
// NOTE: this class is used for error reporting. But in order to pass plugin verification, it should derive directly from java's Annotation
// and implement annotationType method (see #KT-16621 for details).
// TODO: instead of the workaround described above, consider using a sum-type for returning errors from constructAnnotation
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
class InvalidScriptResolverAnnotation(val name: String, val annParams: List<Pair<String?, Any?>>?, val error: Exception? = null) : Annotation, java.lang.annotation.Annotation {
override fun annotationType(): Class<out Annotation> = InvalidScriptResolverAnnotation::class.java
}
@@ -17,6 +17,7 @@ dependencies {
compile(project(":kotlin-scripting-compiler-impl"))
compile(kotlinStdlib())
compileOnly(project(":kotlin-reflect-api"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
testCompile(project(":compiler:frontend"))
@@ -37,8 +38,8 @@ sourceSets {
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
kotlinOptions {
languageVersion = "1.2"
apiVersion = "1.2"
languageVersion = "1.3"
apiVersion = "1.3"
freeCompilerArgs += "-Xskip-metadata-version-check"
}
}
@@ -19,8 +19,9 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import java.io.File
class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
@@ -42,7 +43,7 @@ class JvmCliScriptEvaluationExtension : ScriptEvaluationExtension {
val scriptFile = File(sourcePath)
if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(scriptFile.name)) {
val extensionHint =
if (configuration.get(JVMConfigurationKeys.SCRIPT_DEFINITIONS) == listOf(StandardScriptDefinition)) " (.kts)"
if (configuration.get(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS) == listOf(StandardScriptDefinition)) " (.kts)"
else ""
messageCollector.report(ERROR, "Specify path to the script file$extensionHint as the first argument")
return COMPILATION_ERROR
@@ -6,14 +6,15 @@
package org.jetbrains.kotlin.scripting.compiler.plugin
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import java.io.File
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.scripting.KOTLIN_SCRIPTING_PLUGIN_ID
import org.jetbrains.kotlin.scripting.configuration.KOTLIN_SCRIPTING_PLUGIN_ID
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import java.io.File
object ScriptingConfigurationKeys {
val DISABLE_SCRIPTING_PLUGIN_OPTION: CompilerConfigurationKey<Boolean> =
@@ -63,7 +64,6 @@ class ScriptingCommandLineProcessor : CommandLineProcessor {
"Script resolver environment in key-value pairs (the value could be quoted and escaped)",
required = false, allowMultipleOccurrences = true
)
}
override val pluginId = KOTLIN_SCRIPTING_PLUGIN_ID
@@ -87,9 +87,9 @@ class ScriptingCommandLineProcessor : CommandLineProcessor {
}
SCRIPT_DEFINITIONS_OPTION, LEGACY_SCRIPT_TEMPLATES_OPTION -> {
val currentDefs = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).toMutableList()
val currentDefs = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSES).toMutableList()
currentDefs.addAll(value.split(','))
configuration.put(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, currentDefs)
configuration.put(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSES, currentDefs)
}
SCRIPT_DEFINITIONS_CLASSPATH_OPTION -> {
val currentCP = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSPATH).toMutableList()
@@ -15,11 +15,12 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.configuration.configureScriptDefinitions
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsFromClasspathDiscoverySource
import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath
import org.jetbrains.kotlin.scripting.legacy.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import java.io.File
class ScriptingCompilerConfigurationExtension(val project: MockProject) : CompilerConfigurationExtension {
@@ -39,7 +40,7 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
}
val scriptResolverEnv = configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSES)
if (explicitScriptDefinitions.isNotEmpty()) {
configureScriptDefinitions(
@@ -52,13 +53,16 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
}
// If not disabled explicitly, we should always support at least the standard script definition
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) &&
!configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).contains(StandardScriptDefinition)
!configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).contains(StandardScriptDefinition)
) {
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
)
}
configuration.add(
JVMConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES,
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES,
ScriptDefinitionsFromClasspathDiscoverySource(
configuration.jvmClasspathRoots,
configuration.get(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION) ?: emptyMap(),
@@ -69,15 +73,18 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
// If not disabled explicitly, we should always support at least the standard script definition
if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) &&
StandardScriptDefinition !in configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS)
StandardScriptDefinition !in configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
) {
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
StandardScriptDefinition
)
}
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
scriptDefinitionProvider.setScriptDefinitionsSources(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_SOURCES))
scriptDefinitionProvider.setScriptDefinitions(configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS))
// Register new file extensions
val fileTypeRegistry = FileTypeRegistry.getInstance() as CoreFileTypeRegistry
@@ -92,17 +99,3 @@ class ScriptingCompilerConfigurationExtension(val project: MockProject) : Compil
}
}
fun configureScriptDefinitions(
scriptTemplates: List<String>,
configuration: CompilerConfiguration,
baseClassloader: ClassLoader,
messageCollector: MessageCollector,
scriptResolverEnv: Map<String, Any?>
) {
// TODO: consider using escaping to allow kotlin escaped names in class names
val templatesFromClasspath = loadScriptTemplatesFromClasspath(
scriptTemplates, configuration.jvmClasspathRoots, emptyList(), baseClassloader, scriptResolverEnv, messageCollector
)
configuration.addAll(JVMConfigurationKeys.SCRIPT_DEFINITIONS, templatesFromClasspath.toList())
}
@@ -3,12 +3,12 @@
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.legacy
package org.jetbrains.kotlin.scripting.compiler.plugin.definitions
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionsSource
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.LazyScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.scripting.definitions.StandardScriptDefinition
import kotlin.concurrent.write
open class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
@@ -1,30 +1,19 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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.legacy
package org.jetbrains.kotlin.scripting.compiler.plugin.definitions
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.script.ScriptContentLoader
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptReportSink
import org.jetbrains.kotlin.script.adjustByDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
@@ -3,16 +3,17 @@
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.legacy
package org.jetbrains.kotlin.scripting.compiler.plugin.definitions
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.script.ScriptReportSink
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
import kotlin.script.experimental.dependencies.ScriptReport
internal class CliScriptReportSink(private val messageCollector: MessageCollector) : ScriptReportSink {
internal class CliScriptReportSink(private val messageCollector: MessageCollector) :
ScriptReportSink {
override fun attachReports(scriptFile: VirtualFile, reports: List<ScriptReport>) {
reports.forEach {
messageCollector.report(it.severity.convertSeverity(), it.message, location(scriptFile, it.position))
@@ -17,14 +17,13 @@ import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptReportSink
import org.jetbrains.kotlin.scripting.legacy.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.legacy.CliScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.legacy.CliScriptReportSink
import org.jetbrains.kotlin.scripting.extensions.ScriptExtraImportsProviderExtension
import org.jetbrains.kotlin.scripting.extensions.ScriptingResolveExtension
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptReportSink
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
import org.jetbrains.kotlin.scripting.extensions.*
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
private fun <T> ProjectExtensionDescriptor<T>.registerExtensionIfRequired(project: MockProject, extension: T) {
try {
@@ -0,0 +1,93 @@
/*
* 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.compiler.plugin
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptProviderTest : KtUsefulTestCase() {
@Test
fun testLazyScriptDefinitionsProvider() {
val genDefCounter = AtomicInteger()
val standardDef = FakeScriptDefinition()
val shadedDef = FakeScriptDefinition(".x.kts")
val provider = TestCliScriptDefinitionProvider(standardDef).apply {
setScriptDefinitions(listOf(shadedDef, standardDef))
setScriptDefinitionsSources(listOf(
TestScriptDefinitionSource(
genDefCounter,
".y.kts",
".x.kts"
)
))
}
Assert.assertEquals(0, genDefCounter.get())
provider.isScript("a.kt").let {
Assert.assertFalse(it)
Assert.assertEquals(0, genDefCounter.get())
}
provider.isScript("a.y.kts").let {
Assert.assertTrue(it)
Assert.assertEquals(1, genDefCounter.get())
}
provider.isScript("a.x.kts").let {
Assert.assertTrue(it)
Assert.assertEquals(1, genDefCounter.get())
Assert.assertEquals(1, shadedDef.matchCounter.get())
}
provider.isScript("a.z.kts").let {
Assert.assertTrue(it)
Assert.assertEquals(2, genDefCounter.get())
Assert.assertEquals(1, standardDef.matchCounter.get())
}
provider.isScript("a.ktx").let {
Assert.assertFalse(it)
Assert.assertEquals(2, genDefCounter.get())
}
}
}
private open class FakeScriptDefinition(val suffix: String = ".kts") : KotlinScriptDefinition(ScriptTemplateWithArgs::class) {
val matchCounter = AtomicInteger()
override fun isScript(fileName: String): Boolean = fileName.endsWith(suffix).also {
if (it) matchCounter.incrementAndGet()
}
}
private class TestScriptDefinitionSource(val counter: AtomicInteger, val defGens: Iterable<() -> FakeScriptDefinition>) :
ScriptDefinitionsSource
{
constructor(counter: AtomicInteger, vararg suffixes: String) : this(counter, suffixes.map { {
FakeScriptDefinition(
it
)
} })
override val definitions: Sequence<KotlinScriptDefinition> = sequence {
for (gen in defGens) {
counter.incrementAndGet()
yield(gen())
}
}
}
private class TestCliScriptDefinitionProvider(private val standardDef: KotlinScriptDefinition) : CliScriptDefinitionProvider() {
override fun getDefaultScriptDefinition(): KotlinScriptDefinition = standardDef
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.SCRIPT_DEFINITION_MARKERS_PATH
import org.jetbrains.kotlin.scripting.definitions.discoverScriptTemplatesInClasspath
import org.jetbrains.kotlin.scripting.definitions.loadScriptTemplatesFromClasspath
@@ -166,7 +167,7 @@ class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
addJvmClasspathRoots(runtimeClasspath)
addJvmClasspathRoots(scriptingClasspath)
addJvmClasspathRoot(defsOut)
addAll(JVMConfigurationKeys.SCRIPT_DEFINITIONS, lazyDefs)
addAll(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, lazyDefs)
}
val res = KotlinToJVMBytecodeCompiler.compileBunchOfSources(scriptsCompileEnv)
@@ -7,6 +7,7 @@ plugins {
}
dependencies {
compileOnly(project(":kotlin-scripting-compiler"))
compile(project(":kotlin-scripting-compiler-impl"))
compile(project(":kotlin-scripting-intellij"))
compileOnly(project(":idea:idea-gradle"))
@@ -20,3 +21,6 @@ sourceSets {
}
runtimeJar()
ideaPlugin()
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.scripting.idea.plugin
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.idea.configuration.GradleProjectImportHandler
import org.jetbrains.kotlin.idea.facet.KotlinFacet
import org.jetbrains.kotlin.scripting.KOTLIN_SCRIPTING_PLUGIN_ID
import org.jetbrains.kotlin.scripting.configuration.KOTLIN_SCRIPTING_PLUGIN_ID
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import java.io.File