diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt index 119d34482a2..5d238512969 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt @@ -463,7 +463,7 @@ class K2JVMCompiler : CLICompiler() { messageCollector.report( INFO, "Added script definition $template to configuration: files pattern = \"${def.scriptFilePattern}\", " + - "resolver = ${def.resolver?.javaClass?.name}" + "resolver = ${def.dependencyResolver.javaClass.name}" ) } catch (ex: ClassNotFoundException) { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 7264946b685..f9d183ba40b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -187,7 +187,7 @@ class KotlinCoreEnvironment private constructor( KotlinScriptExternalImportsProvider.getInstance(project)?.let { importsProvider -> configuration.addJvmClasspathRoots( - sourceFiles.mapNotNull(importsProvider::getExternalImports) + sourceFiles.mapNotNull(importsProvider::getScriptDependencies) .flatMap { it.classpath } .distinctBy { it.absolutePath }) } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt index 7fae7ffd808..035b1870c02 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/repl/GenericReplCompiler.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtScriptInitializer import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.script.KotlinScriptDefinition +import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider import java.io.File import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.write @@ -67,7 +68,7 @@ open class GenericReplCompiler(disposable: Disposable, Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder) } - val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, compilerState.lastDependencies) + val newDependencies = KotlinScriptExternalImportsProvider.getInstance(checker.environment.project).getScriptDependencies(psiFile) var classpathAddendum: List? = null if (compilerState.lastDependencies != newDependencies) { compilerState.lastDependencies = newDependencies diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt index 8fb349ff820..f3d9b644819 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/KotlinRemoteReplService.kt @@ -66,7 +66,7 @@ open class KotlinJvmReplService( val cls = classloader.loadClass(templateClassName) val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap()) messageCollector.report(INFO, "New script definition $templateClassName: files pattern = \"${def.scriptFilePattern}\", " + - "resolver = ${def.resolver?.javaClass?.name}") + "resolver = ${def.dependencyResolver.javaClass.name}") return def } catch (ex: ClassNotFoundException) { diff --git a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt index cd779ce6247..32c41875e64 100644 --- a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt +++ b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionFromAnnotatedTemplate.kt @@ -18,17 +18,10 @@ package org.jetbrains.kotlin.script import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.StandardFileSystems -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiManager import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.NameUtils -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -import java.io.File import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter @@ -54,7 +47,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( ?: DEFAULT_SCRIPT_FILE_PATTERN } - val resolver: ScriptDependenciesResolver? by lazy { + override val dependencyResolver: ScriptDependenciesResolver by lazy { val defAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull() } } val legacyDefAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull() } } when { @@ -84,8 +77,8 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( log.warn("[kts] Script def error ${ex.message}") null } - else -> BasicScriptDependenciesResolver() - } + else -> null + } ?: BasicScriptDependenciesResolver() } val samWithReceiverAnnotations: List? by lazy { @@ -93,7 +86,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( ?: takeUnlessError { template.annotations.firstIsInstanceOrNull()?.annotations?.toList() } } - private val acceptedAnnotations: List> by lazy { + override val acceptedAnnotations: List> by lazy { fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean = left.parameters.size == right.parameters.size && @@ -104,7 +97,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate( val resolveMethod = ScriptDependenciesResolver::resolve val resolverMethodAnnotations = - resolver?.let { it::class }?.memberFunctions?.find { function -> + dependencyResolver::class.memberFunctions.find { function -> function.name == resolveMethod.name && sameSignature(function, resolveMethod) }?.annotations?.filterIsInstance() @@ -122,62 +115,7 @@ 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: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? { - return takeUnlessError(reportError = false) { - val fileDeps = resolver?.resolve(makeScriptContents(file, project), environment, ::logScriptDefMessage, previousDependencies) - // TODO: use it as a Future - fileDeps?.get() - } - } - - fun makeScriptContents(file: TF, project: Project) = BasicScriptContents(file, getAnnotations = { - val classLoader = template.java.classLoader - takeUnlessError(reportError = false) { - getAnnotationEntries(file, project) - .mapNotNull { psiAnn -> - // TODO: consider advanced matching using semantic similar to actual resolving - acceptedAnnotations.find { ann -> - psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName } - }?.let { constructAnnotation(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass) } - } - } - ?: emptyList() - }) - - - fun getDependenciesFor(previousDependencies: KotlinScriptExternalDependencies?, scriptContents: ScriptContents): KotlinScriptExternalDependencies? { - return takeUnlessError(reportError = false) { - // TODO: use it as a Future - resolver?.resolve(scriptContents, environment, ::logScriptDefMessage, previousDependencies)?.get() - } - } - - private fun getAnnotationEntries(file: TF, project: Project): Iterable = when (file) { - is PsiFile -> getAnnotationEntriesFromPsiFile(file) - is VirtualFile -> getAnnotationEntriesFromVirtualFile(file, project) - is File -> { - val virtualFile = (StandardFileSystems.local().findFileByPath(file.canonicalPath) - ?: throw IllegalArgumentException("Unable to find file ${file.canonicalPath}")) - getAnnotationEntriesFromVirtualFile(virtualFile, project) - } - else -> throw IllegalArgumentException("Unsupported file type $file") - } - - private fun getAnnotationEntriesFromPsiFile(file: PsiFile) = - (file as? KtFile)?.annotationEntries - ?: throw IllegalArgumentException("Unable to extract kotlin annotations from ${file.name} (${file.fileType})") - - private fun getAnnotationEntriesFromVirtualFile(file: VirtualFile, project: Project): Iterable { - val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file) - ?: throw IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}") - return getAnnotationEntriesFromPsiFile(psiFile) - } - - class BasicScriptContents(myFile: TF, getAnnotations: () -> Iterable) : ScriptContents { - override val file: File? by lazy { getFile(myFile) } - override val annotations: Iterable by lazy { getAnnotations() } - override val text: CharSequence? by lazy { getFileContents(myFile) } - } + override fun getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?) = error("Should not be called") override fun toString(): String = "KotlinScriptDefinitionFromAnnotatedTemplate - ${template.simpleName}" diff --git a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderBase.kt b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderBase.kt new file mode 100644 index 00000000000..c58b935a86c --- /dev/null +++ b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderBase.kt @@ -0,0 +1,88 @@ +/* + * 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.script + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.StandardFileSystems +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 java.io.File +import kotlin.reflect.KClass +import kotlin.script.dependencies.KotlinScriptExternalDependencies +import kotlin.script.dependencies.ScriptContents + +abstract class KotlinScriptExternalImportsProviderBase(private val project: Project): KotlinScriptExternalImportsProvider { + fun getScriptContents(scriptDefinition: KotlinScriptDefinition, file: TF) + = BasicScriptContents(file, getAnnotations = { loadAnnotations(scriptDefinition, file) }) + + private fun loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: TF): List { + val classLoader = scriptDefinition.template.java.classLoader + // TODO_R: report error on failure to load annotation class + return 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) } + } + } + + private fun getAnnotationEntries(file: TF, project: Project): Iterable = when (file) { + is PsiFile -> getAnnotationEntriesFromPsiFile(file) + is VirtualFile -> getAnnotationEntriesFromVirtualFile(file, project) + is File -> { + val virtualFile = (StandardFileSystems.local().findFileByPath(file.canonicalPath) + ?: throw IllegalArgumentException("Unable to find file ${file.canonicalPath}")) + getAnnotationEntriesFromVirtualFile(virtualFile, project) + } + else -> throw IllegalArgumentException("Unsupported file type $file") + } + + private fun getAnnotationEntriesFromPsiFile(file: PsiFile) = + (file as? KtFile)?.annotationEntries + ?: throw IllegalArgumentException("Unable to extract kotlin annotations from ${file.name} (${file.fileType})") + + private fun getAnnotationEntriesFromVirtualFile(file: VirtualFile, project: Project): Iterable { + val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file) + ?: throw IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}") + return getAnnotationEntriesFromPsiFile(psiFile) + } + + class BasicScriptContents(myFile: TF, getAnnotations: () -> Iterable) : ScriptContents { + override val file: File? by lazy { getFile(myFile) } + override val annotations: Iterable by lazy { getAnnotations() } + override val text: CharSequence? by lazy { getFileContents(myFile) } + } + + fun resolveDependencies( + scriptDef: KotlinScriptDefinition, + file: TF, + oldDependencies: KotlinScriptExternalDependencies? = null + ): KotlinScriptExternalDependencies? { + val scriptContents = getScriptContents(scriptDef, file) + return scriptDef.dependencyResolver.resolve( + scriptContents, + (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment, + { _, _, _ -> Unit }, // TODO_R: + oldDependencies + ).get() + } +} + diff --git a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderImpl.kt b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderImpl.kt index 9c0a2cace3c..40217aed9fa 100644 --- a/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderImpl.kt +++ b/compiler/frontend.script/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProviderImpl.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.script -import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import java.io.File @@ -28,22 +27,24 @@ import kotlin.script.dependencies.KotlinScriptExternalDependencies class KotlinScriptExternalImportsProviderImpl( val project: Project, private val scriptDefinitionProvider: KotlinScriptDefinitionProvider -) : KotlinScriptExternalImportsProvider { +) : KotlinScriptExternalImportsProviderBase(project) { private val cacheLock = ReentrantReadWriteLock() private val cache = hashMapOf() - override fun getExternalImports(file: TF): KotlinScriptExternalDependencies? = cacheLock.read { + override fun getScriptDependencies(file: TF): KotlinScriptExternalDependencies? = cacheLock.read { calculateExternalDependencies(file) } - private fun calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? { + private fun calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? { val path = getFilePath(file) val cached = cache[path] - return if (cached != null) cached else { + return if (cached != null) cached + else { val scriptDef = scriptDefinitionProvider.findScriptDefinition(file) if (scriptDef != null) { - val deps = scriptDef.getDependenciesFor(file, project, null) + val deps = resolveDependencies(scriptDef, file) + if (deps != null) { log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}") } @@ -55,11 +56,6 @@ class KotlinScriptExternalImportsProviderImpl( else null } } - - companion object { - @JvmStatic - fun getInstance(project: Project): KotlinScriptExternalImportsProvider? = - ServiceManager.getService(project, KotlinScriptExternalImportsProvider::class.java) - internal val log = Logger.getInstance(KotlinScriptExternalImportsProvider::class.java) - } } + +private val log = Logger.getInstance(KotlinScriptExternalImportsProvider::class.java) \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt index 0d2a1c856d7..66c80f09c69 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt @@ -25,6 +25,7 @@ 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.templates.standard.ScriptTemplateWithArgs open class KotlinScriptDefinition(val template: KClass) { @@ -37,13 +38,20 @@ open class KotlinScriptDefinition(val template: KClass) { open val annotationsForSamWithReceivers: List get() = emptyList() - open fun isScript(file: TF): Boolean = + open fun isScript(file: TF): Boolean = getFileName(file).endsWith(KotlinParserDefinition.STD_SCRIPT_EXT) open fun getScriptName(script: KtScript): Name = NameUtils.getScriptNameForFile(script.containingKtFile.name) - open fun getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? = null + @Deprecated("Use dependencyResolver instead", level = DeprecationLevel.ERROR) + open fun getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? = null + + open val dependencyResolver: ScriptDependenciesResolver = EmptyDependencyResolver + + open val acceptedAnnotations: List> = emptyList() + + private object EmptyDependencyResolver : ScriptDependenciesResolver } object StandardScriptDefinition : KotlinScriptDefinition(ScriptTemplateWithArgs::class) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProvider.kt index 42c16e5f4bb..b145b683317 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptExternalImportsProvider.kt @@ -22,7 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile import kotlin.script.dependencies.KotlinScriptExternalDependencies interface KotlinScriptExternalImportsProvider { - fun getExternalImports(file: TF): KotlinScriptExternalDependencies? + fun getScriptDependencies(file: TF): KotlinScriptExternalDependencies? companion object { fun getInstance(project: Project): KotlinScriptExternalImportsProvider = @@ -31,4 +31,4 @@ interface KotlinScriptExternalImportsProvider { } fun getScriptExternalDependencies(file: VirtualFile, project: Project): KotlinScriptExternalDependencies? = - KotlinScriptExternalImportsProvider.getInstance(project).getExternalImports(file) + KotlinScriptExternalImportsProvider.getInstance(project).getScriptDependencies(file) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ScriptModuleInfos.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ScriptModuleInfos.kt index f2c722d714d..734a89dae7e 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ScriptModuleInfos.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ScriptModuleInfos.kt @@ -29,7 +29,6 @@ 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 org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider import kotlin.script.dependencies.KotlinScriptExternalDependencies class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) { @@ -44,7 +43,7 @@ data class ScriptModuleInfo(val project: Project, val scriptFile: VirtualFile, get() = ModuleOrigin.OTHER val externalDependencies: KotlinScriptExternalDependencies? - get() = KotlinScriptConfigurationManager.getInstance(project).getExternalImports(scriptFile) + get() = KotlinScriptConfigurationManager.getInstance(project).getScriptDependencies(scriptFile) override val name: Name = Name.special("