[K/N lib] IDEA plugin for K/N + missed items in konan-serializer module
This commit is contained in:
committed by
Mikhail Glukhikh
parent
51bb408c56
commit
eda29a48a4
@@ -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)
|
||||
}
|
||||
+121
@@ -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)
|
||||
}
|
||||
+72
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user