Refactor and fixes after review

improving script constructor search algorithm - now default params should be supported
remove prefix Customized- from scripts ModuleInfo-related classes
refactoring ideaModuleInfos after review
refactoring ResolveSessionProvider construction for readability
refactoring KotlinScriptConfigurationManager after review
fixing KotlinScriptDefinitionProvider after review
This commit is contained in:
Ilya Chernikov
2016-06-06 13:16:27 +02:00
parent d5b486eb80
commit 656fcc9775
9 changed files with 61 additions and 118 deletions
@@ -317,8 +317,14 @@ object KotlinToJVMBytecodeCompiler {
catch (e: java.lang.NoSuchMethodException) {
for (ctor in scriptClass.kotlin.constructors) {
val (ctorArgs, scriptArgsLeft) = ctor.parameters.fold(Pair(emptyList<Any>(), scriptArgs), ::foldingFunc)
if (ctorArgs.size == ctor.parameters.size && (scriptArgsLeft == null || scriptArgsLeft.isEmpty()))
return ctor.call(*ctorArgs.toTypedArray())
if (ctorArgs.size <= ctor.parameters.size && (scriptArgsLeft == null || scriptArgsLeft.isEmpty())) {
val argsMap = ctorArgs.zip(ctor.parameters) { a, p -> Pair(p, a) }.toMap()
try {
return ctor.callBy(argsMap)
}
catch (e: Exception) { // TODO: find the exact exception type thrown then callBy fails
}
}
}
}
return null
@@ -92,7 +92,7 @@ private fun tryFindKotlinPathsForScriptClasspathEnvVars(project: Project): Kotli
.map { it() }
.firstOrNull { it.runtimePath.exists() }
private fun generateKotlinScriptClasspathEnvVarsFromPaths(project: Project, paths: KotlinPaths?): Map<String, List<String>> =
fun generateKotlinScriptClasspathEnvVarsFromPaths(project: Project, paths: KotlinPaths?): Map<String, List<String>> =
mapOf("kotlin-runtime" to (paths?.run { listOf(runtimePath.canonicalPath) } ?: emptyList()),
"kotlin-reflect" to (paths?.run { listOf(reflectPath.canonicalPath) } ?: emptyList()),
"project-root" to listOf(project.basePath ?: "."),
@@ -65,7 +65,9 @@ class KotlinScriptDefinitionProvider {
}
fun removeScriptDefinition(scriptDefinition: KotlinScriptDefinition) {
definitions.remove(scriptDefinition)
definitionsLock.write {
definitions.remove(scriptDefinition)
}
}
companion object {
@@ -290,40 +290,40 @@ internal object NotUnderContentRootModuleInfo : IdeaModuleInfo {
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
}
class CustomizedScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
override fun equals(other: Any?) = other is CustomizedScriptModuleSearchScope && scriptFile == other.scriptFile && super.equals(other)
class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
override fun equals(other: Any?) = other is ScriptModuleSearchScope && scriptFile == other.scriptFile && super.equals(other)
override fun hashCode() = scriptFile.hashCode() * 73 * super.hashCode()
}
internal data class CustomizedScriptModuleInfo(val project: Project, val module: Module?, val virtualFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition,
val scriptExtraImports: List<KotlinScriptExtraImport>) : IdeaModuleInfo {
internal data class ScriptModuleInfo(val project: Project, val module: Module?, val scriptFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition,
val scriptExtraImports: List<KotlinScriptExtraImport>) : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
override val name: Name = Name.special("<$SCRIPT_NAME_PREFIX${scriptDefinition.name}>")
override fun contentScope(): CustomizedScriptModuleSearchScope {
override fun contentScope(): ScriptModuleSearchScope {
val dependenciesRoots = dependenciesRoots()
return CustomizedScriptModuleSearchScope(
virtualFile,
return ScriptModuleSearchScope(
scriptFile,
if (dependenciesRoots.isEmpty()) GlobalSearchScope.EMPTY_SCOPE
else GlobalSearchScope.union(dependenciesRoots.map { FileLibraryScope(project, it) }.toTypedArray()))
}
private fun dependenciesRoots(): List<VirtualFile> {
// TODO: find out whether it should be cashed (some changes listener should be implemented for the cached roots)
val virtualFileManager = VirtualFileManager.getInstance()
val jarfs = StandardFileSystems.jar()
return (scriptDefinition.getScriptDependenciesClasspath() + scriptExtraImports.flatMap { it.classpath })
.map { File(it).canonicalFile }
.distinct()
.map {
.mapNotNull {
// TODO: ensure that the entries are checked elsewhere, so diagnostics is delivered to a user if files are not correctly specified
if (it.isFile)
jarfs.findFileByPath(it.absolutePath + URLUtil.JAR_SEPARATOR) ?: throw FileNotFoundException("Classpath entry points to a file that is not a JAR archive: ${it.canonicalPath}")
jarfs.findFileByPath(it.absolutePath + URLUtil.JAR_SEPARATOR) ?: null // diag: Classpath entry points to a file that is not a JAR archive
else
virtualFileManager.findFileByUrl("file://${it.absolutePath}") ?: throw FileNotFoundException("Classpath entry points to a non-existent location: ${it.canonicalPath}")
StandardFileSystems.local().findFileByPath(it.absolutePath) ?: null // diag: Classpath entry points to a non-existent location
}
}
@@ -337,7 +337,6 @@ internal data class CustomizedScriptModuleInfo(val project: Project, val module:
}
class FileLibraryScope(project: Project, private val libraryRoot: VirtualFile) : GlobalSearchScope(project) {
val cachedEntries : MutableSet<VirtualFile> = hashSetOf()
override fun contains(file: VirtualFile): Boolean =
VfsUtilCore.isAncestor(libraryRoot, file, false)
@@ -353,53 +352,6 @@ class FileLibraryScope(project: Project, private val libraryRoot: VirtualFile) :
override fun hashCode() = libraryRoot.hashCode()
}
private data class FakeFileLibrary(val libraryRoot: VirtualFile) : Library {
override fun getFiles(rootType: OrderRootType): Array<out VirtualFile> = arrayOf(libraryRoot)
override fun getUrls(rootType: OrderRootType): Array<out String> = arrayOf(libraryRoot.url)
override fun getName(): String? = libraryRoot.name
override fun getModifiableModel(): Library.ModifiableModel { throw UnsupportedOperationException() }
override fun getTable(): LibraryTable? = null
override fun getRootProvider(): RootProvider { throw UnsupportedOperationException() }
override fun isJarDirectory(url: String): Boolean { throw UnsupportedOperationException() }
override fun isJarDirectory(url: String, rootType: OrderRootType): Boolean { throw UnsupportedOperationException() }
override fun isValid(url: String, rootType: OrderRootType): Boolean { throw UnsupportedOperationException() }
override fun readExternal(element: Element?) { throw UnsupportedOperationException() }
override fun writeExternal(element: Element?) { throw UnsupportedOperationException() }
override fun dispose() { }
}
private class FileLibraryModuleInfo(project: Project, val libraryRoot: VirtualFile) : LibraryInfo(project, FakeFileLibrary(libraryRoot)) {
override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER
override val name: Name = Name.special("<$LIBRARY_NAME_PREFIX${libraryRoot.nameWithoutExtension}>")
override fun contentScope() = FileLibraryScope(project, libraryRoot)
override fun dependencies(): List<IdeaModuleInfo> = emptyList()
override fun toString() = "FileLibraryModuleInfo(root=${libraryRoot.name})"
override fun equals(other: Any?): Boolean {
if (this === other) return true
return (other is FileLibraryModuleInfo && libraryRoot == other.libraryRoot)
}
override fun hashCode(): Int = 47 * libraryRoot.hashCode()
}
private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
@@ -121,52 +121,46 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
}
val dependenciesForSyntheticFileCache = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, filesModificationTracker)
val debugName = "completion/highlighting in $syntheticFileModule for files ${files.joinToString { it.name }} for platform $targetPlatform"
fun makeGlobalResolveSessionProvider(reuseDataFrom: ProjectResolutionFacade? = null,
moduleFilter: (IdeaModuleInfo) -> Boolean = { true }
): CachedValueProvider.Result<ModuleResolverProvider> {
return globalResolveSessionProvider(
debugName,
project,
targetPlatform,
sdk,
commonGlobalContext = globalContext,
syntheticFiles = files,
reuseDataFrom = reuseDataFrom,
moduleFilter = moduleFilter,
dependencies = dependenciesForSyntheticFileCache
)
}
return when {
syntheticFileModule is ModuleSourceInfo -> {
val dependentModules = syntheticFileModule.getDependentModules()
ProjectResolutionFacade(project, globalContext.storageManager) {
globalResolveSessionProvider(
debugName,
project,
targetPlatform,
sdk,
commonGlobalContext = globalContext,
syntheticFiles = files,
makeGlobalResolveSessionProvider(
reuseDataFrom = globalFacade(targetPlatform, sdk),
moduleFilter = { it in dependentModules },
dependencies = dependenciesForSyntheticFileCache
)
moduleFilter = { it in dependentModules })
}
}
syntheticFileModule is CustomizedScriptModuleInfo -> {
syntheticFileModule is ScriptModuleInfo -> {
ProjectResolutionFacade(project, globalContext.storageManager) {
globalResolveSessionProvider(
debugName,
project,
targetPlatform,
sdk,
commonGlobalContext = globalContext,
syntheticFiles = files,
reuseDataFrom = librariesFacade(targetPlatform, sdk),
moduleFilter = { true },
dependencies = dependenciesForSyntheticFileCache
makeGlobalResolveSessionProvider(
reuseDataFrom = librariesFacade(targetPlatform, sdk)
)
}
}
syntheticFileModule is LibrarySourceInfo || syntheticFileModule is NotUnderContentRootModuleInfo -> {
ProjectResolutionFacade(project, globalContext.storageManager) {
globalResolveSessionProvider(
debugName,
project,
targetPlatform,
sdk,
commonGlobalContext = globalContext,
syntheticFiles = files,
makeGlobalResolveSessionProvider(
reuseDataFrom = librariesFacade(targetPlatform, sdk),
moduleFilter = { it == syntheticFileModule },
dependencies = dependenciesForSyntheticFileCache
moduleFilter = { it == syntheticFileModule }
)
}
}
@@ -177,16 +171,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
// (file under both classes and sources root)
LOG.warn("Creating cache with synthetic files ($files) in classes of library $syntheticFileModule")
ProjectResolutionFacade(project, globalContext.storageManager) {
globalResolveSessionProvider(
debugName,
project,
targetPlatform,
sdk,
commonGlobalContext = globalContext,
syntheticFiles = files,
moduleFilter = { true },
dependencies = dependenciesForSyntheticFileCache
)
makeGlobalResolveSessionProvider()
}
}
@@ -125,7 +125,7 @@ private fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFil
val scriptDefinition = getScriptDefinition(virtualFile, project)
if (scriptDefinition != null)
return CustomizedScriptModuleInfo(project, module, virtualFile, scriptDefinition, getScriptExtraImports(virtualFile, project))
return ScriptModuleInfo(project, module, virtualFile, scriptDefinition, getScriptExtraImports(virtualFile, project))
return NotUnderContentRootModuleInfo
}
@@ -32,7 +32,7 @@ fun getResolveScope(file: KtFile): GlobalSearchScope {
return when (file.getModuleInfo()) {
is ModuleSourceInfo -> KotlinSourceFilterScope.sourceAndClassFiles(file.resolveScope, file.project)
is CustomizedScriptModuleInfo -> file.getModuleInfo().contentScope()
is ScriptModuleInfo -> file.getModuleInfo().contentScope()
else -> GlobalSearchScope.EMPTY_SCOPE
}
}
@@ -30,6 +30,7 @@ import com.intellij.util.indexing.IndexableSetContributor
import com.intellij.util.io.URLUtil
import org.jetbrains.kotlin.idea.caches.resolve.FileLibraryScope
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.io.FileNotFoundException
import java.lang.ref.WeakReference
@@ -44,7 +45,7 @@ class KotlinScriptConfigurationManager(project: Project,
private val kotlinScriptDependenciesIndexableSetContributor: KotlinScriptDependenciesIndexableSetContributor?
) : AbstractProjectComponent(project) {
private val kotlinEnvVars: Map<String, List<String>> by lazy { generateKotlinScriptClasspathEnvVars(myProject) }
private val kotlinEnvVars: Map<String, List<String>> by lazy { generateKotlinScriptClasspathEnvVarsFromPaths(myProject, PathUtil.getKotlinPathsForIdeaPlugin()) }
init {
reloadScriptDefinitions()
@@ -57,7 +58,7 @@ class KotlinScriptConfigurationManager(project: Project,
override fun after(events: List<VFileEvent>) {
val changedExtraImportConfigs = ArrayList<VirtualFile>()
var anyScriptDefinitionChanged = false
events.filter { it is VFileEvent }.forEach {
events.forEach {
it.file?.let {
if (isScriptDefinitionConfigFile(it)) {
anyScriptDefinitionChanged = true
@@ -155,10 +156,8 @@ class KotlinScriptConfigurationManager(project: Project,
class KotlinScriptDependenciesIndexableSetContributor : IndexableSetContributor() {
override fun getAdditionalProjectRootsToIndex(project: Project): Set<VirtualFile> {
return super.getAdditionalProjectRootsToIndex(project) +
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspath()
}
override fun getAdditionalProjectRootsToIndex(project: Project): Set<VirtualFile> =
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspath().toSet()
override fun getAdditionalRootsToIndex(): Set<VirtualFile> = emptySet()
}
@@ -25,11 +25,10 @@ import com.intellij.psi.PsiClass
import com.intellij.psi.search.EverythingGlobalScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.containers.ConcurrentFactoryMap
import org.jetbrains.kotlin.idea.caches.resolve.CustomizedScriptModuleSearchScope
import org.jetbrains.kotlin.idea.caches.resolve.ScriptModuleSearchScope
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
@Suppress("unused") // project extension
class KotlinScriptDependenciesClassFinder(project: Project,
private val kotlinScriptConfigurationManager: KotlinScriptConfigurationManager
) : NonClasspathClassFinder(project), KotlinSafeClassFinder {
@@ -46,8 +45,8 @@ class KotlinScriptDependenciesClassFinder(project: Project,
override fun calcClassRoots(): List<VirtualFile> = kotlinScriptConfigurationManager.getAllScriptsClasspath()
override fun getCache(scope: GlobalSearchScope?): PackageDirectoryCache =
(scope as? CustomizedScriptModuleSearchScope ?:
(scope as? JavaClassFinderImpl.DelegatingGlobalSearchScopeWithBaseAccess)?.base as? CustomizedScriptModuleSearchScope
(scope as? ScriptModuleSearchScope ?:
(scope as? JavaClassFinderImpl.FilterOutKotlinSourceFilesScope)?.base as? ScriptModuleSearchScope
)?.let {
myCaches.get(it.scriptFile)
} ?: super.getCache(scope)
@@ -60,8 +59,8 @@ class KotlinScriptDependenciesClassFinder(project: Project,
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? =
super.findClass(qualifiedName, scope)?.let { aClass ->
when {
scope is CustomizedScriptModuleSearchScope ||
(scope as? JavaClassFinderImpl.DelegatingGlobalSearchScopeWithBaseAccess)?.base is CustomizedScriptModuleSearchScope ||
scope is ScriptModuleSearchScope ||
(scope as? JavaClassFinderImpl.FilterOutKotlinSourceFilesScope)?.base is ScriptModuleSearchScope ||
scope is EverythingGlobalScope ||
aClass.containingFile?.virtualFile.let { file ->
file != null &&