Update project code to use new script.runtime APIs

This commit is contained in:
Pavel V. Talanov
2017-06-22 23:14:16 +03:00
parent 375b058a28
commit 65dba3615c
14 changed files with 191 additions and 241 deletions
@@ -26,7 +26,7 @@ import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependencies
class KotlinScriptExternalImportsProviderImpl(
val project: Project,
@@ -34,13 +34,13 @@ class KotlinScriptExternalImportsProviderImpl(
) : KotlinScriptExternalImportsProviderBase(project) {
private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, KotlinScriptExternalDependencies?>()
private val cache = hashMapOf<String, ScriptDependencies?>()
override fun getScriptDependencies(file: VirtualFile): KotlinScriptExternalDependencies? = cacheLock.read {
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? = cacheLock.read {
calculateExternalDependencies(file)
}
private fun calculateExternalDependencies(file: VirtualFile): KotlinScriptExternalDependencies? {
private fun calculateExternalDependencies(file: VirtualFile): ScriptDependencies? {
val path = file.path
val cached = cache[path]
return if (cached != null) cached
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.psi.KtFile
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependencies
class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
@@ -70,5 +70,5 @@ class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val
val analyzerEngine = ReplCodeAnalyzer(environment)
var lastDependencies: KotlinScriptExternalDependencies? = null
var lastDependencies: ScriptDependencies? = null
}
@@ -17,8 +17,6 @@
package org.jetbrains.kotlin.script
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.psi.KtScript
@@ -28,15 +26,13 @@ import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.primaryConstructor
import kotlin.script.dependencies.BasicScriptDependenciesResolver
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.DependenciesResolver
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.templates.AcceptedAnnotations
open class KotlinScriptDefinitionFromAnnotatedTemplate(
template: KClass<out Any>,
providedResolver: ScriptDependenciesResolver? = null,
providedResolver: DependenciesResolver? = null,
providedScriptFilePattern: String? = null,
val environment: Map<String, Any?>? = null
) : KotlinScriptDefinition(template) {
@@ -48,38 +44,45 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
?: DEFAULT_SCRIPT_FILE_PATTERN
}
override val dependencyResolver: ScriptDependenciesResolver by lazy {
override val dependencyResolver: DependenciesResolver by lazy {
computeResolver(template, providedResolver)
}
private fun computeResolver(
template: KClass<out Any>,
providedResolver: DependenciesResolver?
): @Suppress("DEPRECATION") DependenciesResolver {
val defAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() } }
val legacyDefAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.ScriptTemplateDefinition>() } }
when {
providedResolver != null -> providedResolver
// TODO: logScriptDefMessage missing or invalid constructor
defAnn != null ->
try {
defAnn.resolver.primaryConstructor?.call() ?: null.apply {
log.warn("[kts] No default constructor found for ${defAnn.resolver.qualifiedName}")
}
}
catch (ex: ClassCastException) {
log.warn("[kts] Script def error ${ex.message}")
null
}
legacyDefAnn != null ->
try {
log.warn("[kts] Deprecated annotations on the script template are used, please update the provider")
legacyDefAnn.resolver.primaryConstructor?.call()?.let {
LegacyScriptDependenciesResolverWrapper(it)
}
?: null.apply {
log.warn("[kts] No default constructor found for ${legacyDefAnn.resolver.qualifiedName}")
}
}
catch (ex: ClassCastException) {
log.warn("[kts] Script def error ${ex.message}")
null
}
else -> null
} ?: BasicScriptDependenciesResolver()
val legacyDefAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>() } }
return when {
providedResolver != null -> providedResolver
// TODO: logScriptDefMessage missing or invalid constructor
defAnn != null ->
try {
defAnn.resolver.primaryConstructor?.call() as? DependenciesResolver ?: null.apply {
log.warn("[kts] No default constructor found for ${defAnn.resolver.qualifiedName}")
}
}
catch (ex: ClassCastException) {
log.warn("[kts] Script def error ${ex.message}")
null
}
legacyDefAnn != null ->
try {
log.warn("[kts] Deprecated annotations on the script template are used, please update the provider")
legacyDefAnn.resolver.primaryConstructor?.call()?.let {
LegacyScriptDependenciesResolverWrapper(it)
}
?: null.apply {
log.warn("[kts] No default constructor found for ${legacyDefAnn.resolver.qualifiedName}")
}
}
catch (ex: ClassCastException) {
log.warn("[kts] Script def error ${ex.message}")
null
}
else -> null
} ?: DependenciesResolver.NoDependencies
}
val samWithReceiverAnnotations: List<String>? by lazy {
@@ -116,8 +119,6 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
// 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 getDependenciesFor(file: VirtualFile, project: Project, previousDependencies: KotlinScriptExternalDependencies?) = error("Should not be called")
override fun toString(): String = "KotlinScriptDefinitionFromAnnotatedTemplate - ${template.simpleName}"
override val annotationsForSamWithReceivers: List<String>
@@ -140,21 +141,4 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
companion object {
internal val log = Logger.getInstance(KotlinScriptDefinitionFromAnnotatedTemplate::class.java)
}
}
internal fun logScriptDefMessage(reportSeverity: ScriptDependenciesResolver.ReportSeverity, s: String, position: ScriptContents.Position?): Unit {
val msg = (position?.run { "[at $line:$col]" } ?: "") + s
when (reportSeverity) {
ScriptDependenciesResolver.ReportSeverity.ERROR -> KotlinScriptDefinitionFromAnnotatedTemplate.log.error(msg)
ScriptDependenciesResolver.ReportSeverity.WARNING -> KotlinScriptDefinitionFromAnnotatedTemplate.log.warn(msg)
ScriptDependenciesResolver.ReportSeverity.INFO -> KotlinScriptDefinitionFromAnnotatedTemplate.log.info(msg)
ScriptDependenciesResolver.ReportSeverity.DEBUG -> KotlinScriptDefinitionFromAnnotatedTemplate.log.debug(msg)
}
}
internal fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean =
left.parameters.size == right.parameters.size &&
left.parameters.zip(right.parameters).all {
it.first.kind == KParameter.Kind.INSTANCE ||
it.first.type == it.second.type
}
}
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import kotlin.reflect.KClass
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.ScriptDependencies
abstract class KotlinScriptExternalImportsProviderBase(private val project: Project): KotlinScriptExternalImportsProvider {
fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: VirtualFile)
@@ -58,16 +58,13 @@ abstract class KotlinScriptExternalImportsProviderBase(private val project: Proj
fun resolveDependencies(
scriptDef: KotlinScriptDefinition,
file: VirtualFile,
oldDependencies: KotlinScriptExternalDependencies? = null
): KotlinScriptExternalDependencies? {
file: VirtualFile
): ScriptDependencies? {
val scriptContents = getScriptContents(scriptDef, file)
return scriptDef.dependencyResolver.resolve(
scriptContents,
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment,
{ _, _, _ -> Unit }, // TODO_R:
oldDependencies
).get()
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment ?: emptyMap()
).dependencies
}
}
@@ -20,6 +20,9 @@ import java.io.File
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import kotlin.reflect.KClass
import kotlin.script.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.dependencies.ScriptDependencies
import kotlin.script.dependencies.ScriptReport
const val DEFAULT_SCRIPT_FILE_PATTERN = ".*\\.kts"
@@ -115,41 +118,42 @@ private fun<T: Comparable<T>> compareIterables(a: Iterable<T>, b: Iterable<T>):
private inline fun Int.chainCompare(compFn: () -> Int ): Int = if (this != 0) this else compFn()
class LegacyScriptDependenciesResolverWrapper(val legacyResolver: ScriptDependenciesResolver) : kotlin.script.dependencies.ScriptDependenciesResolver {
@Suppress("DEPRECATION")
class LegacyScriptDependenciesResolverWrapper(val legacyResolver: ScriptDependenciesResolver) : kotlin.script.dependencies.DependenciesResolver {
override fun resolve(script: kotlin.script.dependencies.ScriptContents,
environment: Map<String, Any?>?,
report: (kotlin.script.dependencies.ScriptDependenciesResolver.ReportSeverity, String, kotlin.script.dependencies.ScriptContents.Position?) -> Unit,
previousDependencies: kotlin.script.dependencies.KotlinScriptExternalDependencies?
): Future<kotlin.script.dependencies.KotlinScriptExternalDependencies?> {
override fun resolve(
scriptContents: kotlin.script.dependencies.ScriptContents,
environment: Map<String, Any?>
): ResolveResult {
val reports = ArrayList<ScriptReport>()
val legacyDeps = legacyResolver.resolve(
object : ScriptContents {
override val file: File? get() = script.file
override val annotations: Iterable<Annotation> get() = script.annotations
override val text: CharSequence? get() = script.text
override val file: File? get() = scriptContents.file
override val annotations: Iterable<Annotation> get() = scriptContents.annotations
override val text: CharSequence? get() = scriptContents.text
},
environment,
{ sev, msg, pos -> report(kotlin.script.dependencies.ScriptDependenciesResolver.ReportSeverity.values()[sev.ordinal],
msg,
pos?.let { kotlin.script.dependencies.ScriptContents.Position(it.line, it.col) }) },
previousDependencies?.let {
object : KotlinScriptExternalDependencies {
override val javaHome get() = it.javaHome
override val classpath get() = it.classpath
override val imports get() = it.imports
override val sources get() = it.sources
override val scripts get() = it.scripts
}
}
).get()
return kotlin.script.dependencies.PseudoFuture(legacyDeps?.let {
object : kotlin.script.dependencies.KotlinScriptExternalDependencies {
override val javaHome get() = it.javaHome
override val classpath get() = it.classpath
override val imports get() = it.imports
override val sources get() = it.sources
override val scripts get() = it.scripts
}
})
{ sev, msg, pos ->
reports.add(ScriptReport(msg, sev.convertSeverity(), pos?.convertPosition()))
}, null
).get() ?: return ResolveResult.Failure()
val dependencies = ScriptDependencies(
javaHome = legacyDeps.javaHome?.let(::File),
classpath = legacyDeps.classpath.toList(),
imports = legacyDeps.imports.toList(),
sources = legacyDeps.sources.toList(),
scripts = legacyDeps.scripts.toList()
)
return ResolveResult.Success(dependencies, reports)
}
private fun ScriptDependenciesResolver.ReportSeverity.convertSeverity(): ScriptReport.Severity = when(this) {
ScriptDependenciesResolver.ReportSeverity.ERROR -> ScriptReport.Severity.ERROR
ScriptDependenciesResolver.ReportSeverity.WARNING -> ScriptReport.Severity.WARNING
ScriptDependenciesResolver.ReportSeverity.INFO -> ScriptReport.Severity.INFO
ScriptDependenciesResolver.ReportSeverity.DEBUG -> ScriptReport.Severity.DEBUG
}
private fun ScriptContents.Position.convertPosition(): ScriptReport.Position = ScriptReport.Position(line, col)
}
@@ -22,7 +22,7 @@ import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import java.io.File
import java.net.URLClassLoader
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.dependencies.DependenciesResolver
interface ScriptTemplatesProvider {
@@ -38,7 +38,7 @@ interface ScriptTemplatesProvider {
val templateClassNames: Iterable<String>
val resolver: ScriptDependenciesResolver? get() = null
val resolver: DependenciesResolver? get() = null
val filePattern: String? get() = null
@@ -17,16 +17,13 @@
package org.jetbrains.kotlin.script
import com.intellij.openapi.fileTypes.LanguageFileType
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
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.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.dependencies.DependenciesResolver
import kotlin.script.templates.standard.ScriptTemplateWithArgs
open class KotlinScriptDefinition(val template: KClass<out Any>) {
@@ -45,14 +42,9 @@ open class KotlinScriptDefinition(val template: KClass<out Any>) {
open fun getScriptName(script: KtScript): Name =
NameUtils.getScriptNameForFile(script.containingKtFile.name)
@Deprecated("Use dependencyResolver instead", level = DeprecationLevel.ERROR)
open fun getDependenciesFor(file: VirtualFile, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? = null
open val dependencyResolver: DependenciesResolver get() = DependenciesResolver.NoDependencies
open val dependencyResolver: ScriptDependenciesResolver = EmptyDependencyResolver
open val acceptedAnnotations: List<KClass<out Annotation>> = emptyList()
private object EmptyDependencyResolver : ScriptDependenciesResolver
open val acceptedAnnotations: List<KClass<out Annotation>> get() = emptyList()
}
object StandardScriptDefinition : KotlinScriptDefinition(ScriptTemplateWithArgs::class)
@@ -20,10 +20,10 @@ 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.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependencies
interface KotlinScriptExternalImportsProvider {
fun getScriptDependencies(file: VirtualFile): KotlinScriptExternalDependencies?
fun getScriptDependencies(file: VirtualFile): ScriptDependencies?
fun getScriptDependencies(file: PsiFile) = getScriptDependencies(file.virtualFile)
companion object {
@@ -32,5 +32,5 @@ interface KotlinScriptExternalImportsProvider {
}
}
fun getScriptExternalDependencies(file: VirtualFile, project: Project): KotlinScriptExternalDependencies? =
fun getScriptExternalDependencies(file: VirtualFile, project: Project): ScriptDependencies? =
KotlinScriptExternalImportsProvider.getInstance(project).getScriptDependencies(file)
@@ -35,13 +35,10 @@ import java.net.URLClassLoader
import java.nio.charset.Charset
import java.rmi.server.UnicastRemoteObject
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.dependencies.asFuture
import kotlin.script.dependencies.*
import kotlin.script.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.templates.ScriptTemplateDefinition
import kotlin.test.fail
@@ -817,18 +814,13 @@ internal val File.loggerCompatiblePath: String
if (OSKind.current == OSKind.Windows) absolutePath.replace('\\', '/')
else absolutePath
open class TestKotlinScriptDummyDependenciesResolver : ScriptDependenciesResolver {
open class TestKotlinScriptDummyDependenciesResolver : DependenciesResolver {
override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?,
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?>
{
return object : KotlinScriptExternalDependencies {
override val classpath: Iterable<File> = classpathFromClassloader()
override val imports: Iterable<String> = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo")
}.asFuture()
override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult {
return ScriptDependencies(
classpath = classpathFromClassloader(),
imports = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo")
).asSuccess()
}
}
@@ -44,12 +44,9 @@ import java.lang.Exception
import java.lang.reflect.InvocationTargetException
import java.net.URL
import java.net.URLClassLoader
import java.util.concurrent.Future
import kotlin.reflect.KClass
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.dependencies.asFuture
import kotlin.script.dependencies.*
import kotlin.script.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.templates.AcceptedAnnotations
import kotlin.script.templates.ScriptTemplateDefinition
import kotlin.script.templates.standard.ScriptTemplateWithArgs
@@ -320,19 +317,17 @@ class ScriptTemplateTest {
}
}
open class TestKotlinScriptDummyDependenciesResolver : ScriptDependenciesResolver {
open class TestKotlinScriptDummyDependenciesResolver : DependenciesResolver {
@AcceptedAnnotations(DependsOn::class, DependsOnTwo::class)
override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?,
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?>
override fun resolve(scriptContents: ScriptContents,
environment: Environment
): ResolveResult
{
return object : KotlinScriptExternalDependencies {
override val classpath: Iterable<File> = classpathFromClassloader()
override val imports: Iterable<String> = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo")
}.asFuture()
return ScriptDependencies(
classpath = classpathFromClassloader(),
imports = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo")
).asSuccess()
}
protected fun classpathFromClassloader(): List<File> =
@@ -347,13 +342,11 @@ class TestKotlinScriptDependenciesResolver : TestKotlinScriptDummyDependenciesRe
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
@AcceptedAnnotations(DependsOn::class, DependsOnTwo::class)
override fun resolve(script: ScriptContents,
environment: Map<String, Any?>?,
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?>
override fun resolve(scriptContents: ScriptContents,
environment: Environment
): ResolveResult
{
val cp = script.annotations.flatMap {
val cp = scriptContents.annotations.flatMap {
when (it) {
is DependsOn -> if (it.path == "@{runtime}") listOf(kotlinPaths.runtimePath, kotlinPaths.scriptRuntimePath) else listOf(File(it.path))
is DependsOnTwo -> listOf(it.path1, it.path2).flatMap {
@@ -367,10 +360,10 @@ class TestKotlinScriptDependenciesResolver : TestKotlinScriptDummyDependenciesRe
else -> throw Exception("Unknown annotation ${it::class.java}")
}
}
return object : KotlinScriptExternalDependencies {
override val classpath: Iterable<File> = classpathFromClassloader() + cp
override val imports: Iterable<String> = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo")
}.asFuture()
return ScriptDependencies(
classpath = classpathFromClassloader() + cp,
imports = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo")
).asSuccess()
}
}
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependencies
class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
override fun equals(other: Any?) = other is ScriptModuleSearchScope && scriptFile == other.scriptFile && super.equals(other)
@@ -42,7 +42,7 @@ data class ScriptModuleInfo(val project: Project, val scriptFile: VirtualFile,
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
val externalDependencies: KotlinScriptExternalDependencies?
val externalDependencies: ScriptDependencies?
get() = KotlinScriptConfigurationManager.getInstance(project).getScriptDependencies(scriptFile)
override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>")
@@ -56,13 +56,13 @@ data class ScriptModuleInfo(val project: Project, val scriptFile: VirtualFile,
}
}
private fun sdkDependencies(scriptDependencies: KotlinScriptExternalDependencies?, project: Project): List<SdkInfo>
private fun sdkDependencies(scriptDependencies: ScriptDependencies?, project: Project): List<SdkInfo>
= listOfNotNull(findJdk(scriptDependencies, project)?.let { SdkInfo(project, it) })
fun findJdk(dependencies: KotlinScriptExternalDependencies?, project: Project): Sdk? {
fun findJdk(dependencies: ScriptDependencies?, project: Project): Sdk? {
val allJdks = getAllProjectSdks()
// workaround for mismatched gradle wrapper and plugin version
val javaHome = try { dependencies?.javaHome } catch (e: Throwable) { null }
val javaHome = try { dependencies?.javaHome?.canonicalPath } catch (e: Throwable) { null }
return allJdks.find { javaHome != null && it.homePath == javaHome } ?:
ProjectRootManager.getInstance(project).projectSdk ?:
@@ -113,8 +113,8 @@ data class ScriptDependenciesSourceModuleInfo(
}
private class ScriptBinariesScopeCache(private val project: Project) : SLRUCache<KotlinScriptExternalDependencies, GlobalSearchScope>(6, 6) {
override fun createValue(key: KotlinScriptExternalDependencies?): GlobalSearchScope {
private class ScriptBinariesScopeCache(private val project: Project) : SLRUCache<ScriptDependencies, GlobalSearchScope>(6, 6) {
override fun createValue(key: ScriptDependencies?): GlobalSearchScope {
val roots = key?.classpath ?: emptyList()
val classpath = KotlinScriptConfigurationManager.toVfsRoots(roots)
// TODO: this is not very efficient because KotlinSourceFilterScope already checks if the files are in scripts classpath
@@ -34,19 +34,19 @@ import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.search.NonClasspathDirectoriesScope
import com.intellij.util.io.URLUtil
import kotlinx.coroutines.experimental.asCoroutineDispatcher
import kotlinx.coroutines.experimental.future.future
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.script.*
import java.io.File
import java.lang.Math.max
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletableFuture.supplyAsync
import java.util.concurrent.Executors.newFixedThreadPool
import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.function.Supplier
import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependencies
import kotlin.script.dependencies.experimental.AsyncDependenciesResolver
// NOTE: this service exists exclusively because KotlinScriptConfigurationManager
@@ -54,7 +54,7 @@ import kotlin.script.dependencies.KotlinScriptExternalDependencies
class IdeScriptExternalImportsProvider(
private val scriptConfigurationManager: KotlinScriptConfigurationManager
) : KotlinScriptExternalImportsProvider {
override fun getScriptDependencies(file: VirtualFile): KotlinScriptExternalDependencies? {
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? {
return scriptConfigurationManager.getScriptDependencies(file)
}
}
@@ -64,7 +64,7 @@ class KotlinScriptConfigurationManager(
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider
) : KotlinScriptExternalImportsProviderBase(project) {
private val cacheLock = ReentrantReadWriteLock()
private val threadPool = newFixedThreadPool(max(1, Runtime.getRuntime().availableProcessors() / 2))
private val scriptDependencyUpdatesDispatcher = newFixedThreadPool(1).asCoroutineDispatcher()
init {
reloadScriptDefinitions()
@@ -144,17 +144,17 @@ class KotlinScriptConfigurationManager(
scriptDefinitionProvider.setScriptDefinitions(def)
}
private class TimeStampedRequest(val future: CompletableFuture<Unit>, val timeStamp: TimeStamp)
private class TimeStampedRequest(val future: CompletableFuture<*>, val timeStamp: TimeStamp)
private class DataAndRequest(
val dependencies: KotlinScriptExternalDependencies?,
val dependencies: ScriptDependencies?,
val modificationStamp: Long?,
val requestInProgress: TimeStampedRequest? = null
)
private val cache = hashMapOf<String, DataAndRequest?>()
override fun getScriptDependencies(file: VirtualFile): KotlinScriptExternalDependencies = cacheLock.read {
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies = cacheLock.read {
val path = file.path
val cached = cache[path]
cached?.dependencies?.let { return it }
@@ -163,7 +163,7 @@ class KotlinScriptConfigurationManager(
updateExternalImportsCache(listOf(file))
return cache[path]?.dependencies ?: NoDependencies
return cache[path]?.dependencies ?: ScriptDependencies.Empty
}
private fun tryLoadingFromDisk(cached: DataAndRequest?, file: VirtualFile, path: String) {
@@ -189,14 +189,18 @@ class KotlinScriptConfigurationManager(
return cache.remove(file.path) != null
}
// TODO: support apis that allow for async updates for any template
if (scriptDef is KotlinScriptDefinitionFromAnnotatedTemplate) {
return updateAsync(file, scriptDef)
val dependencyResolver = scriptDef.dependencyResolver
if (dependencyResolver is AsyncDependenciesResolver) {
return updateAsync(file, scriptDef, dependencyResolver)
}
return updateSync(file, scriptDef)
}
private fun updateAsync(file: VirtualFile, scriptDefinition: KotlinScriptDefinitionFromAnnotatedTemplate): Boolean {
private fun updateAsync(
file: VirtualFile,
scriptDefinition: KotlinScriptDefinition,
dependencyResolver: AsyncDependenciesResolver
): Boolean {
val path = file.path
val oldDataAndRequest = cache[path]
@@ -206,7 +210,7 @@ class KotlinScriptConfigurationManager(
oldDataAndRequest?.requestInProgress?.future?.cancel(true)
val (currentTimeStamp, newFuture) = sendRequest(path, scriptDefinition, file, oldDataAndRequest)
val (currentTimeStamp, newFuture) = sendRequest(path, scriptDefinition, dependencyResolver, file, oldDataAndRequest)
cache[path] = DataAndRequest(
oldDataAndRequest?.dependencies,
@@ -218,24 +222,29 @@ class KotlinScriptConfigurationManager(
private fun sendRequest(
path: String,
scriptDefinition: KotlinScriptDefinitionFromAnnotatedTemplate,
scriptDef: KotlinScriptDefinition,
dependenciesResolver: AsyncDependenciesResolver,
file: VirtualFile,
oldDataAndRequest: DataAndRequest?
): Pair<TimeStamp, CompletableFuture<Unit>> {
): Pair<TimeStamp, CompletableFuture<*>> {
val currentTimeStamp = TimeStamps.next()
val newFuture = supplyAsync(Supplier {
val newDependencies = resolveDependencies(scriptDefinition, file, oldDataAndRequest?.dependencies) ?: EmptyDependencies
val newFuture = future(scriptDependencyUpdatesDispatcher) {
dependenciesResolver.resolveAsync(
getScriptContents(scriptDef, file),
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
)
}.thenAccept { result ->
cacheLock.read {
val lastTimeStamp = cache[path]?.requestInProgress?.timeStamp
if (lastTimeStamp == currentTimeStamp) {
if (cacheSync(newDependencies, oldDataAndRequest?.dependencies, path, file)) {
if (cacheSync(result.dependencies ?: ScriptDependencies.Empty, oldDataAndRequest?.dependencies, path, file)) {
invalidateLocalCaches()
notifyRootsChanged()
}
}
}
}, threadPool)
}
return Pair(currentTimeStamp, newFuture)
}
@@ -253,13 +262,13 @@ class KotlinScriptConfigurationManager(
private fun updateSync(file: VirtualFile, scriptDef: KotlinScriptDefinition): Boolean {
val path = file.path
val oldDeps = cache[path]?.dependencies
val newDeps = resolveDependencies(scriptDef, file, oldDeps) ?: EmptyDependencies
val newDeps = resolveDependencies(scriptDef, file) ?: ScriptDependencies.Empty
return cacheSync(newDeps, oldDeps, path, file)
}
private fun cacheSync(
new: KotlinScriptExternalDependencies,
old: KotlinScriptExternalDependencies?,
new: ScriptDependencies,
old: ScriptDependencies?,
path: String,
file: VirtualFile
): Boolean {
@@ -277,7 +286,7 @@ class KotlinScriptConfigurationManager(
}
}
private fun save(path: String, new: KotlinScriptExternalDependencies?, virtualFile: VirtualFile, persist: Boolean) {
private fun save(path: String, new: ScriptDependencies?, virtualFile: VirtualFile, persist: Boolean) {
cacheLock.write {
cache.put(path, DataAndRequest(new, virtualFile.modificationStamp))
}
@@ -342,8 +351,6 @@ class KotlinScriptConfigurationManager(
}
}
}
private object NoDependencies: KotlinScriptExternalDependencies
}
private class ClearableLazyValue<out T : Any>(private val lock: ReentrantReadWriteLock, private val compute: () -> T) {
@@ -368,7 +375,7 @@ private class ClearableLazyValue<out T : Any>(private val lock: ReentrantReadWri
}
// TODO: relying on this to compare dependencies seems wrong, doesn't take javaHome and other stuff into account
private fun KotlinScriptExternalDependencies.match(other: KotlinScriptExternalDependencies)
private fun ScriptDependencies.match(other: ScriptDependencies)
= classpath.isSamePathListAs(other.classpath) &&
sources.toSet().isSamePathListAs(other.sources.toSet()) // TODO: gradle returns stdlib and reflect sources in unstable order for some reason
@@ -389,6 +396,4 @@ object TimeStamps {
private var current: Long = 0
fun next() = TimeStamp(current++)
}
private object EmptyDependencies: KotlinScriptExternalDependencies
}
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.idea.caches.FileAttributeService
import java.io.DataInput
import java.io.DataOutput
import java.io.File
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependencies
// TODO_R: write nullable, write version
object ScriptDependenciesFileAttribute {
@@ -40,29 +40,27 @@ object ScriptDependenciesFileAttribute {
fileAttributeService.register(ID, VERSION, false)
}
fun write(virtualFile: VirtualFile, dependencies: KotlinScriptExternalDependencies) {
fun write(virtualFile: VirtualFile, dependencies: ScriptDependencies) {
if (virtualFile !is VirtualFileWithId) return
fileAttributeService.write(virtualFile, ID, dependencies) { output, dep ->
with(dep) {
output.writeFileList(classpath)
output.writeStringList(imports)
// TODO: actually read/write nullable
output.writeString(javaHome ?: "")
output.writeFile(javaHome ?: File(""))
output.writeFileList(scripts)
output.writeFileList(sources)
}
}
}
fun read(virtualFile: VirtualFile): KotlinScriptExternalDependencies? {
fun read(virtualFile: VirtualFile): ScriptDependencies? {
if (virtualFile !is VirtualFileWithId) return null
return fileAttributeService.read(virtualFile, ID) { input ->
SerializedScriptDependencies(
ScriptDependencies(
classpath = input.readFileList(),
imports = input.readStringList(),
javaHome = input.readString(),
javaHome = input.readFile(),
scripts = input.readFileList(),
sources = input.readFileList()
)
@@ -71,19 +69,11 @@ object ScriptDependenciesFileAttribute {
}
private fun DataInput.readStringList(): List<String> = readSeq(this) { readString() }
private fun DataInput.readFileList() = readStringList().map { File(it) }
private fun DataInput.readFileList() = readSeq(this) { readFile() }
private fun DataInput.readString() = readUTF(this)
private fun DataInput.readFile() = readUTF(this).let { File(it) }
private fun DataOutput.writeFileList(iterable: Iterable<File>) = writeSeq(this, iterable.toList()) { writeFile(it) }
private fun DataOutput.writeFile(it: File) = writeString(it.canonicalPath)
private fun DataOutput.writeString(string: String) = writeUTF(this, string)
private fun DataOutput.writeStringList(iterable: Iterable<String>) = writeSeq(this, iterable.toList()) { writeString(it) }
private class SerializedScriptDependencies(
override val classpath: Iterable<File>,
override val imports: Iterable<String>,
override val javaHome: String?,
override val scripts: Iterable<File>,
override val sources: Iterable<File>
) : KotlinScriptExternalDependencies
@@ -24,11 +24,8 @@ import org.jetbrains.kotlin.idea.core.script.dependencies.KotlinScriptResolveSco
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.util.concurrent.Future
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.PseudoFuture
import kotlin.script.dependencies.ScriptContents
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.dependencies.*
import kotlin.script.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class StandardKotlinScriptTemplateProvider(val project: Project) : ScriptTemplatesProvider {
@@ -45,7 +42,7 @@ class StandardKotlinScriptTemplateProvider(val project: Project) : ScriptTemplat
)
}
override val resolver: ScriptDependenciesResolver = BundledKotlinScriptDependenciesResolver()
override val resolver: DependenciesResolver = BundledKotlinScriptDependenciesResolver()
private fun getScriptSDK(project: Project): String? {
val jdk = PathUtilEx.getAnyJdk(project) ?:
@@ -55,26 +52,22 @@ class StandardKotlinScriptTemplateProvider(val project: Project) : ScriptTemplat
}
}
class BundledKotlinScriptDependenciesResolver : ScriptDependenciesResolver {
class BundledKotlinScriptDependenciesResolver : DependenciesResolver {
override fun resolve(
script: ScriptContents,
environment: Map<String, Any?>?,
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies?): Future<KotlinScriptExternalDependencies?> {
val javaHome = environment?.get("sdk") as String?
val dependencies = KotlinBundledScriptDependencies(javaHome)
return PseudoFuture(dependencies)
scriptContents: ScriptContents,
environment: Environment
): ResolveResult {
val javaHome = environment.get("sdk") as String?
val dependencies = ScriptDependencies(
javaHome = javaHome?.let(::File),
classpath = with(PathUtil.getKotlinPathsForIdeaPlugin()) {
listOf(
reflectPath,
stdlibPath,
scriptRuntimePath
)
}
)
return dependencies.asSuccess()
}
}
class KotlinBundledScriptDependencies(override val javaHome: String?) : KotlinScriptExternalDependencies {
override val classpath: Iterable<File> get() {
return with(PathUtil.getKotlinPathsForIdeaPlugin()) {
listOf(
reflectPath,
stdlibPath,
scriptRuntimePath
)
}
}
}