[K/N lib] IDEA plugin for K/N + missed items in konan-serializer module

This commit is contained in:
Dmitriy Dolovov
2018-08-27 17:21:04 +03:00
committed by Mikhail Glukhikh
parent 51bb408c56
commit eda29a48a4
44 changed files with 2230 additions and 21 deletions
+35
View File
@@ -0,0 +1,35 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":konan:konan-serializer"))
// compile("org.jetbrains.kotlin:kotlin-native-gradle-plugin")
compileOnly(project(":idea:idea-gradle"))
compileOnly(project(":idea:idea-native"))
compileOnly(project(":idea")) { isTransitive = false }
compileOnly(project(":idea:idea-jvm"))
compile(project(":idea:kotlin-gradle-tooling"))
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:frontend.script"))
compile(project(":js:js.frontend"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("gradle"))
compileOnly(intellijPluginDep("Groovy"))
compileOnly(intellijPluginDep("junit"))
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
configureInstrumentation()
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.gradle
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.find
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.findAll
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.util.BooleanFunction
import com.sun.media.jfxmediaimpl.platform.PlatformManager
import org.jetbrains.konan.settings.KonanArtifact
import org.jetbrains.konan.settings.KonanModelProvider
import org.jetbrains.kotlin.gradle.plugin.model.KonanModel
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
import org.jetbrains.kotlin.konan.target.PlatformManager
import org.jetbrains.kotlin.konan.target.customerDistribution
import org.jetbrains.plugins.gradle.settings.GradleSettings
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
import java.nio.file.Path
class GradleKonanModelProvider : KonanModelProvider {
override fun reloadLibraries(project: Project, libraryPaths: Collection<Path>): Boolean =
GradleSettings.getInstance(project).linkedProjectsSettings.isNotEmpty()
override fun getKonanHome(project: Project): Path? {
val projectNode = ProjectDataManager.getInstance().getExternalProjectsData(project, GradleConstants.SYSTEM_ID)
.mapNotNull { it.externalProjectStructure }
.firstOrNull() ?: return null
projectNode.getUserData(KONAN_HOME)?.let { return it }
var konanHomePath: Path? = null
find(projectNode, ProjectKeys.MODULE, BooleanFunction { moduleNode ->
find(moduleNode, KonanProjectResolver.KONAN_MODEL_KEY, BooleanFunction { konanModelNode ->
konanHomePath = konanModelNode.data.konanHome.toPath()
projectNode.putUserData(KONAN_HOME, konanHomePath)
konanHomePath != null
}) != null
})
return konanHomePath
}
override fun getArtifacts(project: Project): Collection<KonanArtifact> {
val artifacts = mutableListOf<KonanArtifact>()
ProjectDataManager.getInstance().getExternalProjectsData(project, GradleConstants.SYSTEM_ID)
.mapNotNull { it.externalProjectStructure }
.forEach { projectStructure ->
findAll(projectStructure, ProjectKeys.MODULE)
.map { Pair(it.data.externalName, find<KonanModel>(it, KonanProjectResolver.KONAN_MODEL_KEY)) }
.filter { it.second != null }
.forEach { (moduleName, konanProjectNode) ->
konanProjectNode!!.data.artifacts.forEach { konanArtifact ->
val sources = konanArtifact.srcFiles.map { it.toPath() }
artifacts.add(
KonanArtifact(
konanArtifact.name,
moduleName,
konanArtifact.type,
konanTarget(konanProjectNode.data.konanHome, konanArtifact),
mutableListOf(), sources, konanArtifact.file.toPath()
)
)
}
}
}
return artifacts
}
private fun konanTarget(konanHome: File, konanArtifactEx: KonanModelArtifact) =
PlatformManager(customerDistribution(konanHome.absolutePath)).targetValues.find { it.name == konanArtifactEx.targetPlatform }
companion object {
val KONAN_HOME = Key.create<Path>("KONAN_HOME")
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.gradle
import com.intellij.openapi.project.Project
import org.jetbrains.konan.settings.KonanProjectComponent
import org.jetbrains.plugins.gradle.settings.GradleSettings
class GradleKonanProjectComponent(project: Project) : KonanProjectComponent(project) {
override fun looksLikeKotlinNativeProject(): Boolean {
//TODO not just any gradle project
return GradleSettings.getInstance(project).linkedProjectsSettings.isNotEmpty()
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.gradle
import com.intellij.execution.RunManager
import com.intellij.execution.RunnerAndConfigurationSettings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.Key
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.service.project.IdeModelsProvider
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
import com.intellij.openapi.externalSystem.service.project.manage.AbstractProjectDataService
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.konan.settings.KonanArtifact
import org.jetbrains.konan.settings.KonanModelProvider
import org.jetbrains.konan.settings.isExecutable
import org.jetbrains.kotlin.gradle.plugin.model.KonanModel
import org.jetbrains.plugins.gradle.util.GradleConstants
class KonanProjectDataService : AbstractProjectDataService<KonanModel, Module>() {
override fun getTargetDataKey(): Key<KonanModel> = KonanProjectResolver.KONAN_MODEL_KEY
override fun postProcess(
toImport: Collection<DataNode<KonanModel>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModifiableModelsProvider
) {
}
override fun onSuccessImport(
imported: Collection<DataNode<KonanModel>>,
projectData: ProjectData?,
project: Project,
modelsProvider: IdeModelsProvider
) {
if (projectData?.owner != GradleConstants.SYSTEM_ID) return
project.messageBus.syncPublisher(KonanModelProvider.RELOAD_TOPIC).run()
}
companion object {
@JvmStatic
fun forEachKonanProject(
project: Project,
consumer: (konanProject: KonanModel, moduleData: ModuleData, rootProjectPath: String) -> Unit
) {
for (projectInfo in ProjectDataManager.getInstance().getExternalProjectsData(project, GradleConstants.SYSTEM_ID)) {
val projectStructure = projectInfo.externalProjectStructure ?: continue
val projectData = projectStructure.data
val rootProjectPath = projectData.linkedExternalProjectPath
val modulesNodes = ExternalSystemApiUtil.findAll(projectStructure, ProjectKeys.MODULE)
for (moduleNode in modulesNodes) {
val projectNode = ExternalSystemApiUtil.find<KonanModel>(moduleNode, KonanProjectResolver.KONAN_MODEL_KEY)
if (projectNode != null) {
val konanProject = projectNode.data
val moduleData = moduleNode.data
consumer(konanProject, moduleData, rootProjectPath)
}
}
}
}
}
}
@@ -0,0 +1,157 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.gradle
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.Key
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.*
import com.intellij.util.containers.ContainerUtil.set
import com.intellij.util.io.isDirectory
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.gradle.plugin.model.KonanModel
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.MetaVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.plugins.gradle.service.project.AbstractProjectResolverExtension
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
import java.nio.file.Files.isDirectory
import java.nio.file.Files.walk
import java.nio.file.Path
import java.util.*
/**
* [KonanProjectResolver] creates IDE project model in terms of External System API
*/
class KonanProjectResolver : AbstractProjectResolverExtension() {
// to ask gradle for the model
override fun getExtraProjectModelClasses(): Set<Class<*>> {
return set<Class<*>>(KonanModel::class.java)
}
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
resolverCtx.getExtraProject(gradleModule, KonanModel::class.java)?.let {
// store a local process copy of the object to get rid of proxy types for further serialization
ideModule.createChild(KONAN_MODEL_KEY, MyKonanModel(it))
}
nextResolver.populateModuleExtraModels(gradleModule, ideModule)
}
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
resolverCtx.getExtraProject(gradleModule, KonanModel::class.java)?.let {
val added = mutableSetOf<String>()
for (artifact in it.artifacts) {
for (srcDir in artifact.srcDirs) {
val rootPath = srcDir.absolutePath
if (!added.add(rootPath)) continue
val ideContentRoot = ContentRootData(GradleConstants.SYSTEM_ID, rootPath)
ideContentRoot.storePath(ExternalSystemSourceType.SOURCE, rootPath)
ideModule.createChild(ProjectKeys.CONTENT_ROOT, ideContentRoot)
}
}
}
nextResolver.populateModuleContentRoots(gradleModule, ideModule)
}
// based on KonanCMakeProjectComponent.reloadLibraries but with the multi-module projects support
override fun populateModuleDependencies(
gradleModule: IdeaModule,
ideModule: DataNode<ModuleData>,
ideProject: DataNode<ProjectData>
) {
val konanModelEx = resolverCtx.getExtraProject(gradleModule, KonanModel::class.java)
if (konanModelEx != null) {
val libraryPaths = LinkedHashSet<Path>()
var konanHome: Path? = null
var targetPlatform: String? = null
for (konanArtifact in konanModelEx.artifacts) {
if (konanHome == null) {
konanHome = konanModelEx.konanHome.toPath()
targetPlatform = konanArtifact.targetPlatform
}
konanArtifact.libraries.forEach { libraryPaths.add(it.toPath()) }
}
if (konanHome != null) {
// add konanStdlib copied from KonanPaths.konanStdlib
libraryPaths.add(konanHome.resolve("klib/common/stdlib"))
// add konanPlatformLibraries, copied from KonanPaths.konanPlatformLibraries
if (targetPlatform != null) {
try {
val resolvedTargetName = HostManager.resolveAlias(targetPlatform)
val klibPath = konanHome.resolve("klib/platform/${resolvedTargetName}")
walk(klibPath, 1)
.filter { it.isDirectory() && it.fileName.toString() != "stdlib" && it != klibPath }
.forEach { libraryPaths.add(it) }
} catch (e: Exception) {
LOG.warn("Unable to collect konan platform libraries paths for '$targetPlatform'", e)
}
}
}
val moduleData = ideModule.data
for (path in libraryPaths) {
val library = LibraryData(moduleData.owner, path.fileName.toString())
library.addPath(LibraryPathType.BINARY, if (isDirectory(path)) path.toAbsolutePath().toString() else path.toString())
val data = LibraryDependencyData(moduleData, library, LibraryLevel.MODULE)
ideModule.createChild(ProjectKeys.LIBRARY_DEPENDENCY, data)
}
}
nextResolver.populateModuleDependencies(gradleModule, ideModule, ideProject)
}
private class MyKonanModel(konanModel: KonanModel) : KonanModel {
override val artifacts: List<KonanModelArtifact> = konanModel.artifacts.map { MyKonanArtifactEx(it) }
override val konanHome: File = konanModel.konanHome
override val konanVersion: KonanVersion = MyKonanVersionEx(konanModel.konanVersion)
override val apiVersion: String? = konanModel.apiVersion
override val languageVersion: String? = konanModel.languageVersion
private class MyKonanVersionEx(version: KonanVersion) : KonanVersion {
override val build: Int = version.build
override val maintenance: Int = version.maintenance
override val major: Int = version.major
override val meta: MetaVersion = version.meta
override val minor: Int = version.minor
override fun toString(showMeta: Boolean, showBuild: Boolean): String {
val sb = StringBuilder("$major.$minor.$maintenance")
if (showMeta) sb.append('-').append(meta)
if (showBuild) sb.append('-').append(build)
return sb.toString()
}
}
private class MyKonanArtifactEx(artifact: KonanModelArtifact) : KonanModelArtifact {
override val searchPaths: List<File> = artifact.searchPaths
override val name: String = artifact.name
override val type: CompilerOutputKind = artifact.type
override val targetPlatform: String = artifact.targetPlatform
override val file: File = artifact.file
override val buildTaskName: String = artifact.buildTaskName
override val srcDirs: List<File> = artifact.srcDirs.toList()
override val srcFiles: List<File> = artifact.srcFiles.toList()
override val libraries: List<File> = artifact.libraries.toList()
}
}
companion object {
val KONAN_MODEL_KEY = Key.create(KonanModel::class.java, ProjectKeys.MODULE.processingWeight + 1)
private val LOG = Logger.getInstance(KonanProjectResolver::class.java)
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.gradle.internal;
import com.intellij.codeInspection.LocalInspectionEP;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.Extensions;
public class KotlinNativeIdeInitializer implements ApplicationComponent {
@Override
public void initComponent() {
unregisterGroovyInspections();
}
// There are groovy local inspections which should not be loaded w/o groovy plugin enabled.
// Those plugin definitions should become optional and dependant on groovy plugin.
// This is a temp workaround before it happens.
private static void unregisterGroovyInspections() {
ExtensionPoint<LocalInspectionEP> extensionPoint =
Extensions.getRootArea().getExtensionPoint(LocalInspectionEP.LOCAL_INSPECTION);
for (LocalInspectionEP ep : extensionPoint.getExtensions()) {
if ("Kotlin".equals(ep.groupDisplayName) && "Groovy".equals(ep.language)) {
extensionPoint.unregisterExtension(ep);
}
}
}
}
+25
View File
@@ -0,0 +1,25 @@
plugins {
kotlin("jvm")
}
dependencies {
compile(project(":idea"))
compile(project(":idea:idea-core"))
compile(project(":compiler:frontend"))
compileOnly(intellijDep())
compile(project(":konan:konan-serializer"))
}
sourceSets {
"main" {
projectDefault()
// java.srcDirs("$rootDir/core/runtime.jvm/src")
}
"test" { none() }
}
configureInstrumentation()
runtimeJar {
archiveName = "native-ide.jar"
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan
import com.intellij.ide.highlighter.ArchiveFileType
import com.intellij.ide.util.TipAndTrickBean
import com.intellij.openapi.components.ApplicationComponent
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.fileTypes.FileTypeManager
class KonanApplicationComponent : ApplicationComponent {
override fun getComponentName(): String = "KonanApplicationComponent"
override fun initComponent() {
FileTypeManager.getInstance().associateExtension(ArchiveFileType.INSTANCE, "klib")
val extensionPoint = Extensions.getRootArea().getExtensionPoint(TipAndTrickBean.EP_NAME)
for (name in arrayOf("Kotlin.html", "Kotlin_project.html", "Kotlin_mix.html", "Kotlin_Java_convert.html")) {
TipAndTrickBean.findByFileName(name)?.let {
extensionPoint.unregisterExtension(it)
}
}
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
class KonanDecompiledFile(provider: KotlinDecompiledFileViewProvider, text: (VirtualFile) -> DecompiledText) :
KtDecompiledFile(provider, text)
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan
import com.intellij.openapi.project.Project
import org.jetbrains.konan.settings.KonanPaths
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.KonanTarget
class KonanPluginSearchPathResolver(val project: Project) : SearchPathResolverWithTarget {
private val paths by lazy { KonanPaths(project) }
override val searchRoots: List<File> by lazy {
paths.konanDist()?.let { distPath ->
listOf(KONAN_COMMON_LIBS_PATH, konanSpecificPlatformLibrariesPath(target.toString())).mapNotNull { relativePath ->
distPath.resolve(relativePath).File().takeIf { it.exists }
}
} ?: emptyList()
}
override fun resolve(givenPath: String): File {
val given = File(givenPath)
if (given.isAbsolute) {
found(given)?.apply { return this }
} else {
searchRoots.forEach {
found(File(it, givenPath))?.apply { return this }
}
}
error("Could not find \"$givenPath\" in ${searchRoots.map { it.absolutePath }}.")
}
override fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List<File> {
val result = mutableListOf<File>()
if (!noStdLib) {
result.add(resolve(KONAN_STDLIB_NAME))
}
if (!noDefaultLibs) {
val defaultLibs = searchRoots.flatMap { it.listFiles }
.filterNot { it.name.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT) == KONAN_STDLIB_NAME }
.map { File(it.absolutePath) }
result.addAll(defaultLibs)
}
return result
}
override val target: KonanTarget
get() = paths.target()
private fun found(candidate: File): File? {
fun check(file: File): Boolean =
file.exists && (file.isFile || File(file, "manifest").exists)
val noSuffix = File(candidate.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT))
val withSuffix = File(candidate.path.suffixIfNot(KLIB_FILE_EXTENSION_WITH_DOT))
return when {
check(withSuffix) -> withSuffix
check(noSuffix) -> noSuffix
else -> null
}
}
}
private fun String.suffixIfNot(suffix: String) = if (this.endsWith(suffix)) this else "$this$suffix"
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.SingleRootFileViewProvider
import com.intellij.psi.impl.PsiFileFactoryImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stubs.PsiFileStub
import com.intellij.testFramework.LightVirtualFile
import org.jetbrains.kotlin.analyzer.ModuleContent
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter
import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.konan.library.libraryResolver
import org.jetbrains.kotlin.konan.utils.KonanFactories
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.serialization.konan.KonanResolvedModuleDescriptors
import org.jetbrains.kotlin.storage.StorageManager
import java.io.File.pathSeparatorChar
const val KONAN_CURRENT_ABI_VERSION = 1
fun createFileStub(project: Project, text: String): PsiFileStub<*> {
val virtualFile = LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, text)
virtualFile.language = KotlinLanguage.INSTANCE
SingleRootFileViewProvider.doNotCheckFileSizeLimit(virtualFile)
val psiFileFactory = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
val file = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, false, false)!!
return KtStubElementTypes.FILE.builder.buildStubTree(file) as PsiFileStub<*>
}
fun createLoggingErrorReporter(log: Logger) = LoggingErrorReporter(log)
fun <M : ModuleInfo> destructModuleContent(moduleContent: ModuleContent<M>) =
moduleContent.syntheticFiles to moduleContent.moduleContentScope
fun <M : ModuleInfo> createDeclarationProviderFactory(
project: Project,
moduleContext: ModuleContext,
syntheticFiles: Collection<KtFile>,
moduleInfo: M,
globalSearchScope: GlobalSearchScope?
) = DeclarationProviderFactoryService.createDeclarationProviderFactory(
project,
moduleContext.storageManager,
syntheticFiles,
globalSearchScope!!,
moduleInfo
)
fun Module.createResolvedModuleDescriptors(
storageManager: StorageManager,
builtIns: KotlinBuiltIns,
languageVersionSettings: LanguageVersionSettings
): KonanResolvedModuleDescriptors {
val libraryMap = mutableMapOf<String, LibraryInfo>()
ModuleRootManager.getInstance(this).orderEntries().forEachLibrary { intellijLibrary ->
intellijLibrary.name?.let { name -> libraryMap[name] = LibraryInfo(project, intellijLibrary) }
true
}
val resolvedLibraries =
KonanPluginSearchPathResolver(project).libraryResolver(KONAN_CURRENT_ABI_VERSION).resolveWithDependencies(libraryMap.keys.toList())
return KonanFactories.DefaultResolvedDescriptorsFactory.createResolved(
resolvedLibraries,
storageManager,
builtIns,
languageVersionSettings,
null,
// Preserve capabilities from the original IntelliJ library:
{ konanLibrary -> libraryMap[konanLibrary.pureName]?.capabilities ?: emptyMap() }
)
}
private val KonanLibrary.pureName
get() = libraryName.substringAfterLast(pathSeparatorChar)
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.util.SystemProperties.getUserHome
import com.intellij.util.io.exists
import com.intellij.util.net.IOExceptionDialog
import org.jetbrains.konan.settings.KonanProjectComponent
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.util.DependencyDownloaderHTTPResponseException
import org.jetbrains.kotlin.konan.util.DependencyProcessor
import org.jetbrains.kotlin.konan.util.DependencySource
import java.nio.file.Path
import java.nio.file.Paths
class KotlinNativeToolchain(
val version: String,
val repoUrl: String
) {
private val artifactName = "kotlin-native-$KONAN_OS-$version"
val baseDir: Path get() = Paths.get("${getUserHome()}/.konan/$artifactName")
val konanc: Path get() = baseDir.resolve("bin/konanc")
val cinterop: Path get() = baseDir.resolve("bin/cinterop")
fun ensureExists(project: Project) {
ApplicationManager.getApplication().assertIsDispatchThread()
if (baseDir.exists()) return
for (attempt in 0..3) {
var exception: Throwable? = null
object : Task.Modal(project, "Downloading Kotlin/Native $version", false) {
override fun run(progress: ProgressIndicator) {
DependencyProcessor(
baseDir.parent.toFile(),
repoUrl,
mapOf(artifactName to listOf(DependencySource.Remote.Public)),
customProgressCallback = { _, downloaded, total ->
progress.fraction = downloaded / maxOf(total, downloaded, 1).toDouble()
}
).run()
ApplicationManager.getApplication().invokeLater {
project.getComponent(KonanProjectComponent::class.java).reloadLibraries()
}
}
override fun onThrowable(error: Throwable) {
exception = error
}
}.queue()
val ex = exception
val tryAgain = if (ex == null) {
false
} else {
val details = if (ex is DependencyDownloaderHTTPResponseException) {
"Server returned ${ex.responseCode} when trying to download ${ex.url}."
} else {
LOG.error(ex)
"Unknown error occurred."
}
IOExceptionDialog.showErrorDialog(
"Failed to download Kotlin/Native",
details
)
}
if (!tryAgain) break
}
}
companion object {
fun looksLikeBundledToolchain(path: String): Boolean =
Paths.get(path).startsWith(Paths.get("${getUserHome()}/.konan/"))
private val KONAN_OS = HostManager.simpleOsName()
//todo: fixme
private val BUNDLED_VERSION = "0.8" //bundledFile("kotlin-native-version").readText().trim()
private val BUILD_DIR = "releases"
val BUNDLED = KotlinNativeToolchain(
version = BUNDLED_VERSION,
repoUrl = "https://download.jetbrains.com/kotlin/native/builds/$BUILD_DIR/$BUNDLED_VERSION/$KONAN_OS"
)
private val LOG = Logger.getInstance(KotlinNativeToolchain::class.java)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import org.jetbrains.konan.settings.KonanProjectComponent
class SetupKotlinNativeStartupActivity : StartupActivity {
override fun runActivity(project: Project) {
//todo: looks like without default project component (like disabled Gradle plugin) we will get exception here (that's bad)
if (!KonanProjectComponent.getInstance(project).looksLikeKotlinNativeProject()) return
ensureKotlinNativeExists(project)
}
private fun ensureKotlinNativeExists(project: Project) {
ApplicationManager.getApplication().assertIsDispatchThread()
KotlinNativeToolchain.BUNDLED.ensureExists(project)
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser
import org.jetbrains.konan.createDeclarationProviderFactory
import org.jetbrains.konan.createResolvedModuleDescriptors
import org.jetbrains.konan.destructModuleContent
import org.jetbrains.kotlin.analyzer.*
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.TargetPlatformVersion
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve
import org.jetbrains.kotlin.idea.caches.project.ModuleProductionSourceInfo
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.TargetEnvironment
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
/**
* @author Alefas
*/
class KonanAnalyzerFacade : ResolverForModuleFactory() {
override val targetPlatform: TargetPlatform
get() = KonanPlatform
override fun <M : ModuleInfo> createResolverForModule(
moduleDescriptor: ModuleDescriptorImpl,
moduleContext: ModuleContext,
moduleContent: ModuleContent<M>,
platformParameters: PlatformAnalysisParameters,
targetEnvironment: TargetEnvironment,
resolverForProject: ResolverForProject<M>,
languageVersionSettings: LanguageVersionSettings,
targetPlatformVersion: TargetPlatformVersion
): ResolverForModule {
val (syntheticFiles, moduleContentScope) = destructModuleContent(moduleContent)
val project = moduleContext.project
val declarationProviderFactory = createDeclarationProviderFactory(
project,
moduleContext,
syntheticFiles,
moduleContent.moduleInfo,
moduleContentScope
)
val container = createContainerForLazyResolve(
moduleContext,
declarationProviderFactory,
BindingTraceContext(),
targetPlatform,
TargetPlatformVersion.NoVersion,
targetEnvironment,
languageVersionSettings
)
val packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
val module = (moduleContent.moduleInfo as? ModuleProductionSourceInfo)?.module
val fragmentProviders = mutableListOf(packageFragmentProvider)
if (module != null) {
val moduleDescriptors = module.createResolvedModuleDescriptors(
moduleContext.storageManager,
moduleContext.module.builtIns, // FIXME(ddol): investigate: reuse existing builtIns (from KonanPlatformSupport) or create new one
languageVersionSettings
)
moduleDescriptors.resolvedDescriptors.mapTo(fragmentProviders) { it.packageFragmentProvider }
fragmentProviders.add(moduleDescriptors.forwardDeclarationsModule.packageFragmentProvider)
}
return ResolverForModule(CompositePackageFragmentProvider(fragmentProviders), container)
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.LocalFileSystem
import org.jetbrains.konan.KONAN_CURRENT_ABI_VERSION
import org.jetbrains.konan.settings.KonanPaths
import org.jetbrains.kotlin.analyzer.ResolverForModuleFactory
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.caches.resolve.IdePlatformSupport
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.context.GlobalContextImpl
import org.jetbrains.kotlin.idea.caches.resolve.PlatformAnalysisSettings
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.library.createKonanLibrary
import org.jetbrains.kotlin.konan.utils.KonanFactories.DefaultDeserializedDescriptorFactory
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
/**
* @author Alefas
*/
class KonanPlatformSupport : IdePlatformSupport() {
override val resolverForModuleFactory: ResolverForModuleFactory
get() = KonanAnalyzerFacade()
override val libraryKind: PersistentLibraryKind<*>? = null
override val platform: TargetPlatform
get() = KonanPlatform
override fun createBuiltIns(settings: PlatformAnalysisSettings, sdkContext: GlobalContextImpl) = createKonanBuiltIns(sdkContext)
override fun isModuleForPlatform(module: Module) = true
}
private fun createKonanBuiltIns(sdkContext: GlobalContextImpl): KotlinBuiltIns {
// TODO: it depends on a random project's stdlib, propagate the actual project here
val stdlibLocation = ProjectManager.getInstance().openProjects.asSequence().mapNotNull {
val stdlibPath = KonanPaths.getInstance(it).konanStdlib() ?: return@mapNotNull null
val stdlibVirtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(stdlibPath.toFile()) ?: return@mapNotNull null
stdlibPath to stdlibVirtualFile
}.firstOrNull()
if (stdlibLocation != null) {
val library = createKonanLibrary(stdlibLocation.first.File(), KONAN_CURRENT_ABI_VERSION)
val builtInsModule = DefaultDeserializedDescriptorFactory.createDescriptorAndNewBuiltIns(
library,
LanguageVersionSettingsImpl.DEFAULT,
sdkContext.storageManager
)
return builtInsModule.builtIns
}
return DefaultBuiltIns.Instance
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ApplicationComponent
import com.intellij.openapi.vfs.*
import com.intellij.util.containers.ContainerUtil.createConcurrentWeakValueMap
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
import org.jetbrains.kotlin.serialization.konan.parsePackageFragment
class KonanDescriptorManager : ApplicationComponent {
companion object {
private const val currentAbiVersion = 1
@JvmStatic
fun getInstance(): KonanDescriptorManager = ApplicationManager.getApplication().getComponent(KonanDescriptorManager::class.java)
}
private val protoCache = createConcurrentWeakValueMap<VirtualFile, KonanProtoBuf.LinkDataPackageFragment>()
fun getCachedPackageFragment(virtualFile: VirtualFile): KonanProtoBuf.LinkDataPackageFragment {
return protoCache.computeIfAbsent(virtualFile) {
val bytes = virtualFile.contentsToByteArray(false)
parsePackageFragment(bytes)
}
}
override fun initComponent() {
VirtualFileManager.getInstance().addVirtualFileListener(object : VirtualFileListener {
override fun fileCreated(event: VirtualFileEvent) = invalidateCaches(event.file)
override fun fileDeleted(event: VirtualFileEvent) = invalidateCaches(event.file)
override fun fileMoved(event: VirtualFileMoveEvent) = invalidateCaches(event.file)
override fun contentsChanged(event: VirtualFileEvent) = invalidateCaches(event.file)
override fun propertyChanged(event: VirtualFilePropertyEvent) = invalidateCaches(event.file)
})
}
private fun invalidateCaches(virtualFile: VirtualFile) {
protoCache.remove(virtualFile)
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index;
import org.jetbrains.kotlin.idea.util.KotlinBinaryExtension;
public class KonanMetaBinary extends KotlinBinaryExtension {
public KonanMetaBinary() {
super(KonanMetaFileType.INSTANCE);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.util.indexing.FileBasedIndex
import org.jetbrains.kotlin.idea.vfilefinder.KotlinFileIndexBase
import org.jetbrains.kotlin.name.FqName
class KonanMetaFileIndex
: KotlinFileIndexBase<KonanMetaFileIndex>(KonanMetaFileIndex::class.java) {
companion object {
private const val VERSION = 4
}
/*todo: check version?!*/
private val dataIndexer = indexer { fileContent ->
val fragment = KonanDescriptorManager.getInstance().getCachedPackageFragment(fileContent.file)
FqName(fragment.fqName)
}
// this is to express intention to index all Kotlin/Native metadata files irrespectively to file size
override fun getFileTypesWithSizeLimitNotApplicable() = listOf(KonanMetaFileType)
override fun getInputFilter() = FileBasedIndex.InputFilter { it.fileType === KonanMetaFileType }
override fun getIndexer() = dataIndexer
override fun getVersion() = VERSION
}
@@ -0,0 +1,55 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeConsumer
import com.intellij.openapi.fileTypes.FileTypeFactory
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
import org.jetbrains.kotlin.serialization.konan.KonanSerializerProtocol
import org.jetbrains.kotlin.serialization.konan.NullFlexibleTypeDeserializer
class KonanMetadataDecompiler : KonanMetadataDecompilerBase<KonanMetadataVersion>(
KonanMetaFileType, KonanPlatform, KonanSerializerProtocol, NullFlexibleTypeDeserializer,
KonanMetadataVersion.DEFAULT_INSTANCE, KonanMetadataVersion.INVALID_VERSION, KonanMetaFileType.STUB_VERSION
) {
override fun doReadFile(file: VirtualFile): FileWithMetadata? {
val proto = KonanDescriptorManager.getInstance().getCachedPackageFragment(file)
return FileWithMetadata.Compatible(proto, KonanSerializerProtocol) //todo: check version compatibility
}
}
class KonanMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
override fun isCompatible(): Boolean = true //todo: ?
companion object {
@JvmField
val DEFAULT_INSTANCE = KonanMetadataVersion(1, 1, 0)
@JvmField
val INVALID_VERSION = KonanMetadataVersion()
}
}
object KonanMetaFileType : FileType {
override fun getName() = "KNM"
override fun getDescription() = "Kotlin/Native Metadata"
override fun getDefaultExtension() = "knm"
override fun getIcon() = null
override fun isBinary() = true
override fun isReadOnly() = true
override fun getCharset(file: VirtualFile, content: ByteArray) = null
const val STUB_VERSION = 2
}
class KonanMetaFileTypeFactory : FileTypeFactory() {
override fun createFileTypes(consumer: FileTypeConsumer) = consumer.consume(KonanMetaFileType, KonanMetaFileType.defaultExtension)
}
@@ -0,0 +1,121 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.psi.compiled.ClassFileDecompilers
import org.jetbrains.konan.KonanDecompiledFile
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
import org.jetbrains.kotlin.idea.decompiler.common.createIncompatibleAbiVersionDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
import org.jetbrains.kotlin.serialization.deserialization.getClassId
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.IOException
//todo: Fix in Kotlin plugin
abstract class KonanMetadataDecompilerBase<out V : BinaryVersion>(
private val fileType: FileType,
private val targetPlatform: TargetPlatform,
private val serializerProtocol: SerializerExtensionProtocol,
private val flexibleTypeDeserializer: FlexibleTypeDeserializer,
private val expectedBinaryVersion: V,
private val invalidBinaryVersion: V,
stubVersion: Int
) : ClassFileDecompilers.Full() {
private val stubBuilder = KonanMetadataStubBuilder(stubVersion, fileType, serializerProtocol, ::readFileSafely)
private val renderer = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
protected abstract fun doReadFile(file: VirtualFile): FileWithMetadata?
override fun accepts(file: VirtualFile) = file.fileType == fileType
override fun getStubBuilder() = stubBuilder
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean) =
KotlinDecompiledFileViewProvider(manager, file, physical) { provider -> KonanDecompiledFile(provider, ::buildDecompiledText) }
private fun readFileSafely(file: VirtualFile): FileWithMetadata? {
if (!file.isValid) return null
return try {
doReadFile(file)
} catch (e: IOException) {
// This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries.
// Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException.
// Note that although calling "refresh()" instead of catching an exception would seem more correct here,
// it's not always allowed and also is likely to degrade performance
null
}
}
private fun buildDecompiledText(virtualFile: VirtualFile): DecompiledText {
assert(virtualFile.fileType == fileType) { "Unexpected file type ${virtualFile.fileType}" }
val file = readFileSafely(virtualFile)
return when (file) {
is FileWithMetadata.Incompatible -> createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, file.version)
is FileWithMetadata.Compatible -> decompiledText(file, targetPlatform, serializerProtocol, flexibleTypeDeserializer, renderer)
null -> createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, invalidBinaryVersion)
}
}
}
sealed class FileWithMetadata {
class Incompatible(val version: BinaryVersion) : FileWithMetadata()
open class Compatible(
val proto: KonanProtoBuf.LinkDataPackageFragment,
serializerProtocol: SerializerExtensionProtocol
) : FileWithMetadata() {
val nameResolver = NameResolverImpl(proto.stringTable, proto.nameTable)
val packageFqName = FqName(nameResolver.getPackageFqName(proto.`package`.getExtension(serializerProtocol.packageFqName)))
open val classesToDecompile: List<ProtoBuf.Class> =
proto.classes.classesList.filter { proto ->
val classId = nameResolver.getClassId(proto.fqName)
!classId.isNestedClass && classId !in ClassDeserializer.BLACK_LIST
}
}
}
//todo: this function is extracted for KonanMetadataStubBuilder, that's the difference from Big Kotlin.
fun decompiledText(
file: FileWithMetadata.Compatible, targetPlatform: TargetPlatform,
serializerProtocol: SerializerExtensionProtocol,
flexibleTypeDeserializer: FlexibleTypeDeserializer,
renderer: DescriptorRenderer
): DecompiledText {
val packageFqName = file.packageFqName
val resolver = KonanMetadataDeserializerForDecompiler(
packageFqName, file.proto, file.nameResolver,
targetPlatform, serializerProtocol, flexibleTypeDeserializer
)
val declarations = arrayListOf<DeclarationDescriptor>()
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
for (classProto in file.classesToDecompile) {
val classId = file.nameResolver.getClassId(classProto.fqName)
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
}
return buildDecompiledText(packageFqName, declarations, renderer)
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.konan.createLoggingErrorReporter
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DeserializerForDecompilerBase
import org.jetbrains.kotlin.idea.decompiler.textBuilder.ResolveEverythingToKotlinAnyLocalClassifierResolver
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
import org.jetbrains.kotlin.serialization.deserialization.*
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope
//todo: Fix in Kotlin plugin
class KonanMetadataDeserializerForDecompiler(
packageFqName: FqName,
private val proto: KonanProtoBuf.LinkDataPackageFragment,
private val nameResolver: NameResolver,
override val targetPlatform: TargetPlatform,
serializerProtocol: SerializerExtensionProtocol,
flexibleTypeDeserializer: FlexibleTypeDeserializer
) : DeserializerForDecompilerBase(packageFqName) {
override val builtIns: KotlinBuiltIns get() = DefaultBuiltIns.Instance
override val deserializationComponents: DeserializationComponents
init {
val notFoundClasses = NotFoundClasses(storageManager, moduleDescriptor)
deserializationComponents = DeserializationComponents(
storageManager, moduleDescriptor, DeserializationConfiguration.Default, KonanProtoBasedClassDataFinder(proto, nameResolver),
AnnotationAndConstantLoaderImpl(moduleDescriptor, notFoundClasses, serializerProtocol), packageFragmentProvider,
ResolveEverythingToKotlinAnyLocalClassifierResolver(builtIns), createLoggingErrorReporter(LOG),
LookupTracker.DO_NOTHING, flexibleTypeDeserializer, emptyList(), notFoundClasses, ContractDeserializer.DEFAULT,
extensionRegistryLite = serializerProtocol.extensionRegistry
)
}
override fun resolveDeclarationsInFacade(facadeFqName: FqName): List<DeclarationDescriptor> {
assert(facadeFqName == directoryPackageFqName) {
"Was called for $facadeFqName; only members of $directoryPackageFqName package are expected."
}
val membersScope = DeserializedPackageMemberScope(
createDummyPackageFragment(facadeFqName),
proto.`package`,
nameResolver,
KonanMetadataVersion.DEFAULT_INSTANCE,
containerSource = null,
components = deserializationComponents
) { emptyList() }
return membersScope.getContributedDescriptors().toList()
}
companion object {
private val LOG = Logger.getInstance(KonanMetadataDeserializerForDecompiler::class.java)
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.compiled.ClsStubBuilder
import com.intellij.psi.impl.compiled.ClassFileStubBuilder
import com.intellij.psi.stubs.PsiFileStub
import com.intellij.util.indexing.FileContent
import org.jetbrains.konan.createFileStub
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.createIncompatibleAbiVersionFileStub
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.konan.platform.KonanPlatform
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
import org.jetbrains.kotlin.serialization.konan.NullFlexibleTypeDeserializer
//todo: Fix in Kotlin plugin
open class KonanMetadataStubBuilder(
private val version: Int,
private val fileType: FileType,
private val serializerProtocol: SerializerExtensionProtocol,
private val readFile: (VirtualFile) -> FileWithMetadata?
) : ClsStubBuilder() {
override fun getStubVersion() = ClassFileStubBuilder.STUB_VERSION + version
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
val virtualFile = content.file
assert(virtualFile.fileType == fileType) { "Unexpected file type ${virtualFile.fileType}" }
val file = readFile(virtualFile) ?: return null
return when (file) {
is FileWithMetadata.Incompatible -> createIncompatibleAbiVersionFileStub()
is FileWithMetadata.Compatible -> { //todo: this part is implemented in our own way
val renderer = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
val ktFileText = decompiledText(file, KonanPlatform, serializerProtocol, NullFlexibleTypeDeserializer, renderer)
createFileStub(content.project, ktFileText.text)
}
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.analyser.index
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.serialization.deserialization.ClassData
import org.jetbrains.kotlin.serialization.deserialization.ClassDataFinder
import org.jetbrains.kotlin.serialization.deserialization.getClassId
//todo: Fix in Kotlin plugin
class KonanProtoBasedClassDataFinder(
proto: KonanProtoBuf.LinkDataPackageFragment,
private val nameResolver: NameResolver,
private val classSource: (ClassId) -> SourceElement = { SourceElement.NO_SOURCE }
) : ClassDataFinder {
private val classIdToProto =
proto.classes.classesList.associateBy { klass ->
nameResolver.getClassId(klass.fqName)
}
internal val allClassIds: Collection<ClassId> get() = classIdToProto.keys
override fun findClassData(classId: ClassId): ClassData? {
val classProto = classIdToProto[classId] ?: return null
return ClassData(nameResolver, classProto, KonanMetadataVersion.DEFAULT_INSTANCE, classSource(classId))
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.settings
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.CompilerOutputKind.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.nio.file.Path
data class KonanArtifact(
val targetName: String,
val moduleName: String,
val type: CompilerOutputKind,
val target: KonanTarget?,
val libraryDependencies: List<String>,
val sources: List<Path>,
val output: Path
)
val CompilerOutputKind.isLibrary: Boolean get() = this == LIBRARY || this == DYNAMIC || this == STATIC || this == FRAMEWORK
val CompilerOutputKind.isExecutable: Boolean get() = this == PROGRAM
val CompilerOutputKind.isTest: Boolean get() = false //TODO
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.settings;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.util.messages.Topic;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
import java.util.Collection;
@ApiStatus.Experimental
public interface KonanModelProvider {
Topic<Runnable> RELOAD_TOPIC = new Topic<>("Kotlin/Native Project Model Updater", Runnable.class);
ExtensionPointName<KonanModelProvider> EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.native.konanModelProvider");
@NotNull
Collection<KonanArtifact> getArtifacts(@NotNull Project project);
@Nullable
Path getKonanHome(@NotNull Project project);
boolean reloadLibraries(@NotNull Project project, @NotNull Collection<Path> libraryPaths);
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.settings
import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.project.Project
import com.intellij.util.io.exists
import com.intellij.util.io.isDirectory
import org.jetbrains.konan.KotlinNativeToolchain
import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.nio.file.Files
import java.nio.file.Path
import java.util.stream.Collectors
open class KonanPaths(protected val project: Project) : ProjectComponent {
companion object {
fun getInstance(project: Project): KonanPaths = project.getComponent(KonanPaths::class.java)
fun bundledKonanDist(): Path = KotlinNativeToolchain.BUNDLED.baseDir
}
override fun getComponentName() = "Kotlin/Native Compiler Paths"
fun konanStdlib(): Path? = konanDist()?.resolve(konanCommonLibraryPath(KONAN_STDLIB_NAME))
open fun konanDist(): Path? {
for (provider in KonanModelProvider.EP_NAME.extensions) {
return provider.getKonanHome(project)
}
return bundledKonanDist()
}
open fun libraryPaths(): Set<Path> = emptySet()
fun konanPlatformLibraries(): List<Path> {
val resolvedTargetName = HostManager.resolveAlias(target().name)
val klibPath =
konanDist()?.resolve(konanSpecificPlatformLibrariesPath(resolvedTargetName))?.takeIf { it.exists() } ?: return emptyList()
return Files.walk(klibPath, 1).filter {
it.isDirectory() && it.fileName.toString() != KONAN_STDLIB_NAME && it != klibPath
}.collect(Collectors.toList())
}
//todo: this is wrong, we are not allowing multiple targets in project
open fun target(): KonanTarget = HostManager.host
}
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.konan.settings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.components.ProjectComponent
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.libraries.LibraryTable
import com.intellij.openapi.vfs.*
import org.jetbrains.konan.KotlinNativeToolchain
import org.jetbrains.konan.settings.KonanModelProvider.RELOAD_TOPIC
import org.jetbrains.kotlin.utils.addIfNotNull
import java.nio.file.Path
abstract class KonanProjectComponent(val project: Project) : ProjectComponent {
companion object {
fun getInstance(project: Project): KonanProjectComponent = project.getComponent(KonanProjectComponent::class.java)
}
protected var libraryPaths: Set<String> = emptySet()
override fun getComponentName(): String = "Kotlin/Native Project Component"
override fun projectOpened() {
VirtualFileManager.getInstance().addVirtualFileListener(object : VirtualFileListener {
override fun fileCreated(event: VirtualFileEvent) {
val filePath = event.file.path
if (libraryPaths.contains(filePath)) reloadLibraries()
}
}, project)
val connection = project.messageBus.connect(project)
connection.subscribe(RELOAD_TOPIC, Runnable {
ApplicationManager.getApplication().invokeLater {
KotlinNativeToolchain.BUNDLED.ensureExists(project)
reloadLibraries()
}
})
if (looksLikeKotlinNativeProject()) {
reloadLibraries()
}
}
open fun reloadLibraries(): Unit = synchronized(this) {
val libraryPaths: Set<Path> = collectLibraryPaths()
this.libraryPaths = libraryPaths.asSequence().map(Path::toString).toSet()
// for (konanModelProvider in KonanModelProvider.EP_NAME.extensions) {
// if (konanModelProvider.reloadLibraries(project, libraryPaths)) return
// }
val module = ModuleManager.getInstance(project).modules.firstOrNull() ?: return
val modifiableModel = ModuleRootManager.getInstance(module).modifiableModel
val libraryTable: LibraryTable = modifiableModel.moduleLibraryTable
libraryTable.libraries.forEach { libraryTable.removeLibrary(it) }
val localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val jarFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
for (path in libraryPaths) {
fun createLibrary(vfs: VirtualFileSystem, rootPath: String) {
val root = vfs.refreshAndFindFileByPath(rootPath) ?: return
val library = libraryTable.createLibrary(path.fileName.toString())
val libraryModifiableModel = library.modifiableModel
libraryModifiableModel.addRoot(root, OrderRootType.CLASSES)
runWriteAction {
libraryModifiableModel.commit()
}
}
val libraryRootPath = path.toString()
val libraryRoot: VirtualFile = localFileSystem.refreshAndFindFileByPath(libraryRootPath) ?: continue
// use JAR FS if this is file (*.klib), otherwise - local FS
createLibrary(if (libraryRoot.isDirectory) localFileSystem else jarFileSystem, libraryRootPath)
}
runWriteAction {
modifiableModel.commit()
}
}
protected open fun collectLibraryPaths(): MutableSet<Path> {
val konanPaths = KonanPaths.getInstance(project)
return mutableSetOf<Path>().apply {
addIfNotNull(konanPaths.konanStdlib())
addAll(konanPaths.konanPlatformLibraries())
}
}
abstract fun looksLikeKotlinNativeProject(): Boolean
}
+27
View File
@@ -10,4 +10,31 @@
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<buildSystemTypeDetector implementation="org.jetbrains.kotlin.idea.configuration.GradleDetector"/>
</extensions>
<!-- NATIVE PART -->
<application-components>
<component>
<implementation-class>org.jetbrains.konan.gradle.internal.KotlinNativeIdeInitializer</implementation-class>
</component>
</application-components>
<project-components>
<component>
<interface-class>org.jetbrains.konan.settings.KonanProjectComponent</interface-class>
<implementation-class>org.jetbrains.konan.gradle.GradleKonanProjectComponent</implementation-class>
</component>
</project-components>
<extensions defaultExtensionNs="org.jetbrains.kotlin.native">
<konanModelProvider implementation="org.jetbrains.konan.gradle.GradleKonanModelProvider"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.plugins.gradle">
<projectResolve implementation="org.jetbrains.konan.gradle.KonanProjectResolver"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
<externalProjectDataService implementation="org.jetbrains.konan.gradle.KonanProjectDataService"/>
</extensions>
<!-- /NATIVE PART -->
</idea-plugin>
+37
View File
@@ -0,0 +1,37 @@
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<application-components>
<component>
<implementation-class>org.jetbrains.konan.KonanApplicationComponent</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.konan.analyser.index.KonanDescriptorManager</implementation-class>
</component>
</application-components>
<project-components>
<component>
<implementation-class>org.jetbrains.konan.settings.KonanPaths</implementation-class>
</component>
</project-components>
<extensionPoints>
<extensionPoint qualifiedName="org.jetbrains.kotlin.native.konanModelProvider" interface="org.jetbrains.konan.settings.KonanModelProvider"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<postStartupActivity implementation="org.jetbrains.konan.SetupKotlinNativeStartupActivity"/>
<psi.classFileDecompiler implementation="org.jetbrains.konan.analyser.index.KonanMetadataDecompiler"/>
<fileType.fileViewProviderFactory filetype="KNM"
implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
<filetype.stubBuilder filetype="KNM" implementationClass="com.intellij.psi.impl.compiled.ClassFileStubBuilder"/>
<filetype.decompiler filetype="KNM" implementationClass="com.intellij.psi.impl.compiled.ClassFileDecompiler"/>
<fileTypeFactory implementation="org.jetbrains.konan.analyser.index.KonanMetaFileTypeFactory"/>
<fileBasedIndex implementation="org.jetbrains.konan.analyser.index.KonanMetaFileIndex"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<binaryExtension implementation="org.jetbrains.konan.analyser.index.KonanMetaBinary"/>
<idePlatformSupport implementation="org.jetbrains.konan.analyser.KonanPlatformSupport" order="first"/>
</extensions>
</idea-plugin>
+1
View File
@@ -3097,6 +3097,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
<!-- CIDR-PLUGIN-EXCLUDE-END -->
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
+1
View File
@@ -3097,6 +3097,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
<!-- CIDR-PLUGIN-EXCLUDE-END -->
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
+1
View File
@@ -3095,6 +3095,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
<!-- CIDR-PLUGIN-EXCLUDE-END -->
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>
+1
View File
@@ -3097,6 +3097,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<xi:include href="jvm.xml" xpointer="xpointer(/idea-plugin/*)"/>
<!-- CIDR-PLUGIN-EXCLUDE-END -->
<xi:include href="native.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="tipsAndTricks.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="extensions/ide.xml" xpointer="xpointer(/idea-plugin/*)"/>