From 51bb408c56448ac33f1c38108163de3983bd41e6 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Sat, 25 Aug 2018 18:00:20 +0300 Subject: [PATCH] [K/N lib] add Kotlin/Native deserializer and library reader --- konan/metadata/build.gradle.kts | 3 +- konan/serializer/build.gradle.kts | 20 ++ .../kotlin/builtins/konan/KonanBuiltIns.kt | 16 ++ .../konan/KonanModuleDescriptorFactory.kt | 37 +++ .../konan/KonanModuleDescriptorUtils.kt | 14 + .../descriptors/konan/KonanModuleOrigin.kt | 26 ++ .../impl/KonanModuleDescriptorFactoryImpl.kt | 46 ++++ .../kotlin/konan/library/KonanLibrary.kt | 70 +++++ .../konan/library/KonanLibraryLayout.kt | 48 ++++ .../kotlin/konan/library/KonanLibraryUtils.kt | 42 +++ .../konan/library/KonanMetadataReader.kt | 11 + .../library/impl/DefaultMetadataReaderImpl.kt | 18 ++ .../konan/library/impl/KonanLibraryImpl.kt | 71 +++++ .../library/impl/KonanLibraryLayoutImpl.kt | 85 ++++++ .../library/resolver/KonanLibraryResolver.kt | 64 +++++ .../library/resolver/KonanResolvedLibrary.kt | 32 +++ .../resolver/impl/KonanLibraryResolverImpl.kt | 142 ++++++++++ .../resolver/impl/KonanResolvedLibraryImpl.kt | 40 +++ .../kotlin/konan/utils/KonanFactories.kt | 56 ++++ .../resolve/konan/platform/KonanPlatform.kt | 22 ++ .../platform/KonanPlatformConfigurator.kt | 42 +++ .../konan/KonanClassDataFinder.kt | 34 +++ .../konan/KonanDeserializationUtils.kt | 14 + ...onanDeserializedModuleDescriptorFactory.kt | 40 +++ ...onanDeserializedPackageFragmentsFactory.kt | 29 +++ .../konan/KonanMetadataVersion.kt | 21 ++ .../konan/KonanPackageFragment.kt | 88 +++++++ .../KonanResolvedModuleDescriptorsFactory.kt | 54 ++++ .../konan/KonanSerializerProtocol.kt | 24 ++ .../konan/NullFlexibleTypeDeserializer.kt | 20 ++ ...DeserializedModuleDescriptorFactoryImpl.kt | 130 ++++++++++ ...DeserializedPackageFragmentsFactoryImpl.kt | 138 ++++++++++ ...nanResolvedModuleDescriptorsFactoryImpl.kt | 189 ++++++++++++++ .../org/jetbrains/kotlin/konan/file/File.kt | 245 ++++++++++++++++++ .../konan/library/SearchPathResolver.kt | 24 ++ .../kotlin/konan/properties/Properties.kt | 47 ++++ .../kotlin/konan/util/ProfilingUtils.kt | 23 ++ .../kotlin/konan/util/Substitution.kt | 33 +++ settings.gradle | 2 + 39 files changed, 2058 insertions(+), 2 deletions(-) create mode 100644 konan/serializer/build.gradle.kts create mode 100644 konan/serializer/src/org/jetbrains/kotlin/builtins/konan/KonanBuiltIns.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorFactory.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorUtils.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleOrigin.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/impl/KonanModuleDescriptorFactoryImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/konan/utils/KonanFactories.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatform.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatformConfigurator.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializationUtils.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedModuleDescriptorFactory.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedPackageFragmentsFactory.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanMetadataVersion.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanResolvedModuleDescriptorsFactory.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanSerializerProtocol.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/NullFlexibleTypeDeserializer.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedModuleDescriptorFactoryImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt create mode 100644 konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanResolvedModuleDescriptorsFactoryImpl.kt create mode 100644 konan/utils/src/org/jetbrains/kotlin/konan/file/File.kt create mode 100644 konan/utils/src/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt create mode 100644 konan/utils/src/org/jetbrains/kotlin/konan/properties/Properties.kt create mode 100644 konan/utils/src/org/jetbrains/kotlin/konan/util/ProfilingUtils.kt create mode 100644 konan/utils/src/org/jetbrains/kotlin/konan/util/Substitution.kt diff --git a/konan/metadata/build.gradle.kts b/konan/metadata/build.gradle.kts index af58a55a7f3..5f4cb424411 100644 --- a/konan/metadata/build.gradle.kts +++ b/konan/metadata/build.gradle.kts @@ -8,8 +8,7 @@ description = "Kotlin/Native metadata" jvmTarget = "1.6" dependencies { - compile(project(":compiler:serialization")) - compile(project(":konan:konan-utils")) + compile(project(":core:metadata")) } sourceSets { diff --git a/konan/serializer/build.gradle.kts b/konan/serializer/build.gradle.kts new file mode 100644 index 00000000000..ef838315a96 --- /dev/null +++ b/konan/serializer/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +description = "Kotlin/Native deserializer and library reader" + +jvmTarget = "1.6" + +dependencies { + compile(project(":compiler:frontend")) + compile(project(":compiler:frontend.java")) + compile(project(":konan:konan-metadata")) + compile(project(":konan:konan-utils")) +} + +sourceSets { + "main" { projectDefault() } + "test" {} +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/builtins/konan/KonanBuiltIns.kt b/konan/serializer/src/org/jetbrains/kotlin/builtins/konan/KonanBuiltIns.kt new file mode 100644 index 00000000000..e38fd771d80 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/builtins/konan/KonanBuiltIns.kt @@ -0,0 +1,16 @@ +/* + * 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.kotlin.builtins.konan + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.storage.StorageManager + +class KonanBuiltIns(storageManager: StorageManager) : KotlinBuiltIns(storageManager) { + + override fun getSuspendFunction(parameterCount: Int) = + getBuiltInClassByName(Name.identifier("SuspendFunction$parameterCount")) +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorFactory.kt b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorFactory.kt new file mode 100644 index 00000000000..ada7e1bf0a9 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorFactory.kt @@ -0,0 +1,37 @@ +/* + * 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.kotlin.descriptors.konan + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.storage.StorageManager + +interface KonanModuleDescriptorFactory { + + /** + * Base method for creation of any Kotlin/Native [ModuleDescriptor]. + */ + fun createDescriptor( + name: Name, + storageManager: StorageManager, + builtIns: KotlinBuiltIns, + origin: KonanModuleOrigin, + customCapabilities: Map, Any?> = emptyMap() + ): ModuleDescriptorImpl + + /** + * Please use this method with care: As far as it creates an instance of [KotlinBuiltIns] it should be + * normally used for creation of the very first (e.g. "stdlib") module in the set of created modules. + */ + fun createDescriptorAndNewBuiltIns( + name: Name, + storageManager: StorageManager, + origin: KonanModuleOrigin, + customCapabilities: Map, Any?> = emptyMap() + ): ModuleDescriptorImpl +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorUtils.kt b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorUtils.kt new file mode 100644 index 00000000000..490e342f229 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleDescriptorUtils.kt @@ -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.kotlin.descriptors.konan + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME +import org.jetbrains.kotlin.name.Name + +private val STDLIB_MODULE_NAME = Name.special("<$KONAN_STDLIB_NAME>") + +fun ModuleDescriptor.isKonanStdlib() = name == STDLIB_MODULE_NAME diff --git a/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleOrigin.kt b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleOrigin.kt new file mode 100644 index 00000000000..2723bbd7f52 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/KonanModuleOrigin.kt @@ -0,0 +1,26 @@ +/* + * 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.kotlin.descriptors.konan + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.konan.library.KonanLibrary + +sealed class KonanModuleOrigin { + + companion object { + val CAPABILITY = ModuleDescriptor.Capability("KonanModuleOrigin") + } +} + +sealed class CompiledKonanModuleOrigin: KonanModuleOrigin() + +class DeserializedKonanModuleOrigin(val library: KonanLibrary) : CompiledKonanModuleOrigin() + +object CurrentKonanModuleOrigin: CompiledKonanModuleOrigin() + +object SyntheticModulesOrigin : KonanModuleOrigin() + +val ModuleDescriptor.konanModuleOrigin get() = this.getCapability(KonanModuleOrigin.CAPABILITY)!! diff --git a/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/impl/KonanModuleDescriptorFactoryImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/impl/KonanModuleDescriptorFactoryImpl.kt new file mode 100644 index 00000000000..30cd3ccc454 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/descriptors/konan/impl/KonanModuleDescriptorFactoryImpl.kt @@ -0,0 +1,46 @@ +/* + * 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.kotlin.descriptors.konan.impl + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.konan.KonanModuleDescriptorFactory +import org.jetbrains.kotlin.descriptors.konan.KonanModuleOrigin +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.storage.StorageManager + +internal object KonanModuleDescriptorFactoryImpl : KonanModuleDescriptorFactory { + + override fun createDescriptor( + name: Name, + storageManager: StorageManager, + builtIns: KotlinBuiltIns, + origin: KonanModuleOrigin, + customCapabilities: Map, Any?> + ) = ModuleDescriptorImpl( + name, + storageManager, + builtIns, + capabilities = customCapabilities + mapOf(KonanModuleOrigin.CAPABILITY to origin) + ) + + override fun createDescriptorAndNewBuiltIns( + name: Name, + storageManager: StorageManager, + origin: KonanModuleOrigin, + customCapabilities: Map, Any?> + ): ModuleDescriptorImpl { + + val builtIns = KonanBuiltIns(storageManager) + + val moduleDescriptor = createDescriptor(name, storageManager, builtIns, origin, customCapabilities) + builtIns.builtInsModule = moduleDescriptor + + return moduleDescriptor + } +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt new file mode 100644 index 00000000000..f6c590bedc7 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibrary.kt @@ -0,0 +1,70 @@ +/* + * 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.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.properties.propertyList +import org.jetbrains.kotlin.name.FqName + +const val KLIB_PROPERTY_ABI_VERSION = "abi_version" +const val KLIB_PROPERTY_UNIQUE_NAME = "unique_name" +const val KLIB_PROPERTY_LINKED_OPTS = "linkerOpts" +const val KLIB_PROPERTY_DEPENDS = "depends" +const val KLIB_PROPERTY_INTEROP = "interop" +const val KLIB_PROPERTY_PACKAGE = "package" +const val KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS = "exportForwardDeclarations" +const val KLIB_PROPERTY_INCLUDED_HEADERS = "includedHeaders" + +/** + * An abstraction for getting access to the information stored inside of Kotlin/Native library. + */ +interface KonanLibrary { + + val libraryName: String + val libraryFile: File + + // Whether this library is default (provided by Kotlin/Native distribution)? + val isDefault: Boolean + + // Properties: + val manifestProperties: Properties + val abiVersion: String + val linkerOpts: List + + // Paths: + val bitcodePaths: List + val includedPaths: List + + val targetList: List + + val dataFlowGraph: ByteArray? + val moduleHeaderData: ByteArray + fun packageMetadata(fqName: String): ByteArray +} + +val KonanLibrary.uniqueName + get() = manifestProperties.getProperty(KLIB_PROPERTY_UNIQUE_NAME)!! + +val KonanLibrary.unresolvedDependencies: List + get() = manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS) + +val KonanLibrary.isInterop + get() = manifestProperties.getProperty(KLIB_PROPERTY_INTEROP) == "true" + +val KonanLibrary.packageFqName + get() = manifestProperties.getProperty(KLIB_PROPERTY_PACKAGE)?.let { FqName(it) } + +val KonanLibrary.exportForwardDeclarations + get() = manifestProperties.getProperty(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS) + .split(' ').asSequence() + .map { it.trim() } + .filter { it.isNotEmpty() } + .map { FqName(it) } + .toList() + +val KonanLibrary.includedHeaders + get() = manifestProperties.getProperty(KLIB_PROPERTY_INCLUDED_HEADERS).split(' ') diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt new file mode 100644 index 00000000000..92c75db0960 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt @@ -0,0 +1,48 @@ +/* + * 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.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.target.KonanTarget + +/** + * This scheme describes the Kotlin/Native Library (KLIB) layout. + */ +interface KonanLibraryLayout { + + val libraryName: String + + val libDir: File + val target: KonanTarget? + // This is a default implementation. Can't make it an assignment. + get() = null + + val manifestFile + get() = File(libDir, "manifest") + val resourcesDir + get() = File(libDir, "resources") + + val targetsDir + get() = File(libDir, "targets") + val targetDir + get() = File(targetsDir, target!!.visibleName) + + val kotlinDir + get() = File(targetDir, "kotlin") + val nativeDir + get() = File(targetDir, "native") + val includedDir + get() = File(targetDir, "included") + + val linkdataDir + get() = File(libDir, "linkdata") + val moduleHeaderFile + get() = File(linkdataDir, "module") + val dataFlowGraphFile + get() = File(linkdataDir, "module_data_flow_graph") + + fun packageFile(packageName: String) = File(linkdataDir, if (packageName == "") "root_package.knm" else "package_$packageName.knm") +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt new file mode 100644 index 00000000000..6cbd23a91ce --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt @@ -0,0 +1,42 @@ +/* + * 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.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.impl.DefaultMetadataReaderImpl +import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl +import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryChecks +import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryRoot +import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolver +import org.jetbrains.kotlin.konan.library.resolver.impl.KonanLibraryResolverImpl +import org.jetbrains.kotlin.konan.target.KonanTarget + +fun File.unpackZippedKonanLibraryTo(newDir: File) { + + // First, run validity checks for the given KLIB file. + zippedKonanLibraryChecks(this) + + if (newDir.exists) { + if (newDir.isDirectory) + newDir.deleteRecursively() + else + newDir.delete() + } + + zippedKonanLibraryRoot(this).recursiveCopyTo(newDir) + check(newDir.exists) { "Could not unpack $this as $newDir." } +} + +fun createKonanLibrary( + libraryFile: File, + currentAbiVersion: Int, + target: KonanTarget? = null, + isDefault: Boolean = false, + metadataReader: MetadataReader = DefaultMetadataReaderImpl +): KonanLibrary = KonanLibraryImpl(libraryFile, currentAbiVersion, target, isDefault, metadataReader) + +fun SearchPathResolverWithTarget.libraryResolver(abiVersion: Int): KonanLibraryResolver = + KonanLibraryResolverImpl(this, abiVersion) diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt new file mode 100644 index 00000000000..72c960d895f --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt @@ -0,0 +1,11 @@ +/* + * 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.kotlin.konan.library + +interface MetadataReader { + fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray + fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String): ByteArray +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt new file mode 100644 index 00000000000..a469cb4d2b4 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt @@ -0,0 +1,18 @@ +/* + * 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.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.library.KonanLibraryLayout +import org.jetbrains.kotlin.konan.library.MetadataReader + +internal object DefaultMetadataReaderImpl : MetadataReader { + + override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray = + libraryLayout.moduleHeaderFile.readBytes() + + override fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String): ByteArray = + libraryLayout.packageFile(fqName).readBytes() +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt new file mode 100644 index 00000000000..45ea041b377 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt @@ -0,0 +1,71 @@ +/* + * 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.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_ABI_VERSION +import org.jetbrains.kotlin.konan.library.KLIB_PROPERTY_LINKED_OPTS +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.MetadataReader +import org.jetbrains.kotlin.konan.properties.Properties +import org.jetbrains.kotlin.konan.properties.loadProperties +import org.jetbrains.kotlin.konan.properties.propertyList +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.util.defaultTargetSubstitutions +import org.jetbrains.kotlin.konan.util.substitute + +internal class KonanLibraryImpl( + override val libraryFile: File, + private val currentAbiVersion: Int, + internal val target: KonanTarget?, + override val isDefault: Boolean, + private val metadataReader: MetadataReader +) : KonanLibrary { + + // For the zipped libraries inPlace gives files from zip file system + // whereas realFiles extracts them to /tmp. + // For unzipped libraries inPlace and realFiles are the same + // providing files in the library directory. + private val inPlace = createKonanLibraryLayout(libraryFile, target) + private val realFiles = inPlace.realFiles + + override val libraryName + get() = inPlace.libraryName + + override val manifestProperties: Properties by lazy { + val properties = inPlace.manifestFile.loadProperties() + if (target != null) substitute(properties, defaultTargetSubstitutions(target)) + properties + } + + override val abiVersion: String + get() { + val manifestAbiVersion = manifestProperties.getProperty(KLIB_PROPERTY_ABI_VERSION) + check(currentAbiVersion.toString() == manifestAbiVersion) { + "ABI version mismatch. Compiler expects: $currentAbiVersion, the library is $manifestAbiVersion" + } + return manifestAbiVersion + } + + override val linkerOpts: List + get() = manifestProperties.propertyList(KLIB_PROPERTY_LINKED_OPTS, target!!.visibleName) + + override val bitcodePaths: List + get() = (realFiles.kotlinDir.listFilesOrEmpty + realFiles.nativeDir.listFilesOrEmpty).map { it.absolutePath } + + override val includedPaths: List + get() = realFiles.includedDir.listFilesOrEmpty.map { it.absolutePath } + + override val targetList by lazy { inPlace.targetsDir.listFiles.map { it.name } } + + override val dataFlowGraph by lazy { inPlace.dataFlowGraphFile.let { if (it.exists) it.readBytes() else null } } + + override val moduleHeaderData: ByteArray by lazy { metadataReader.loadSerializedModule(inPlace) } + + override fun packageMetadata(fqName: String) = metadataReader.loadSerializedPackageFragment(inPlace, fqName) + + override fun toString() = "$libraryName[default=$isDefault]" +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt new file mode 100644 index 00000000000..348a4b1ba72 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/impl/KonanLibraryLayoutImpl.kt @@ -0,0 +1,85 @@ +/* + * 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.kotlin.konan.library.impl + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.file.asZipRoot +import org.jetbrains.kotlin.konan.file.createTempDir +import org.jetbrains.kotlin.konan.file.createTempFile +import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION +import org.jetbrains.kotlin.konan.library.KLIB_FILE_EXTENSION_WITH_DOT +import org.jetbrains.kotlin.konan.library.KonanLibraryLayout +import org.jetbrains.kotlin.konan.target.KonanTarget + +private class ZippedKonanLibraryLayout(val klibFile: File, override val target: KonanTarget?) : KonanLibraryLayout { + + init { + zippedKonanLibraryChecks(klibFile) + } + + override val libraryName = klibFile.path.removeSuffix(KLIB_FILE_EXTENSION_WITH_DOT) + + override val libDir by lazy { zippedKonanLibraryRoot(klibFile) } +} + +internal fun zippedKonanLibraryChecks(klibFile: File) { + check(klibFile.exists) { "Could not find $klibFile." } + check(klibFile.isFile) { "Expected $klibFile to be a regular file." } + + val extension = klibFile.extension + check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) { "Unexpected file extension: $extension" } +} + +internal fun zippedKonanLibraryRoot(klibFile: File) = klibFile.asZipRoot + +private class UnzippedKonanLibraryLayout(override val libDir: File, override val target: KonanTarget?) : KonanLibraryLayout { + override val libraryName = libDir.path +} + +/** + * This class automatically extracts pieces of the library on first access. Use it if you need + * to pass extracted files to an external tool. Otherwise, stick to [ZippedKonanLibraryLayout]. + */ +private class FileExtractor(zippedLibraryLayout: KonanLibraryLayout) : KonanLibraryLayout by zippedLibraryLayout { + + override val manifestFile: File by lazy { extract(super.manifestFile) } + + override val resourcesDir: File by lazy { extractDir(super.resourcesDir) } + + override val includedDir: File by lazy { extractDir(super.includedDir) } + + override val kotlinDir: File by lazy { extractDir(super.kotlinDir) } + + override val nativeDir: File by lazy { extractDir(super.nativeDir) } + + override val linkdataDir: File by lazy { extractDir(super.linkdataDir) } + + fun extract(file: File): File { + val temporary = createTempFile(file.name) + file.copyTo(temporary) + temporary.deleteOnExit() + return temporary + } + + fun extractDir(directory: File): File { + val temporary = createTempDir(directory.name) + directory.recursiveCopyTo(temporary) + temporary.deleteOnExitRecursively() + return temporary + } +} + +internal fun createKonanLibraryLayout(klib: File, target: KonanTarget? = null) = + if (klib.isFile) ZippedKonanLibraryLayout(klib, target) else UnzippedKonanLibraryLayout(klib, target) + +internal val KonanLibraryLayout.realFiles + get() = when (this) { + is ZippedKonanLibraryLayout -> FileExtractor(this) + // Unpacked library just provides its own files. + is UnzippedKonanLibraryLayout -> this + else -> error("Provide an extractor for your container.") + } + diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt new file mode 100644 index 00000000000..3fb37e694e1 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanLibraryResolver.kt @@ -0,0 +1,64 @@ +/* + * 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.kotlin.konan.library.resolver + +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.SearchPathResolverWithTarget + +typealias DuplicatedLibraryLogger = (String) -> Unit + +interface KonanLibraryResolver { + + val searchPathResolver: SearchPathResolverWithTarget + val abiVersion: Int + + /** + * Given the list of Kotlin/Native library names, ABI version and other parameters + * resolves libraries and evaluates dependencies between them. + */ + fun resolveWithDependencies( + libraryNames: List, + noStdLib: Boolean = false, + noDefaultLibs: Boolean = false, + logger: DuplicatedLibraryLogger? = null + ): KonanLibraryResolveResult +} + +interface KonanLibraryResolveResult { + + fun filterRoots(predicate: (KonanResolvedLibrary) -> Boolean): KonanLibraryResolveResult + + fun getFullList(order: LibraryOrder? = null): List + + fun forEach(action: (KonanLibrary, PackageAccessedHandler) -> Unit) +} + + +typealias LibraryOrder = (Iterable) -> List + +val TopologicalLibraryOrder: LibraryOrder = { input -> + val sorted = mutableListOf() + val visited = mutableSetOf() + val tempMarks = mutableSetOf() + + fun visit(node: KonanResolvedLibrary, result: MutableList) { + if (visited.contains(node)) return + if (tempMarks.contains(node)) error("Cyclic dependency in library graph.") + tempMarks.add(node) + node.resolvedDependencies.forEach { + visit(it, result) + } + visited.add(node) + result += node + } + + input.forEach next@{ + if (visited.contains(it)) return@next + visit(it, sorted) + } + + sorted +} \ No newline at end of file diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt new file mode 100644 index 00000000000..22c9bd44a91 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/KonanResolvedLibrary.kt @@ -0,0 +1,32 @@ +/* + * 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.kotlin.konan.library.resolver + +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.name.FqName + +interface PackageAccessedHandler { + + fun markPackageAccessed(fqName: FqName) +} + +/** + * A [KonanLibrary] wrapper that is used for resolving library's dependencies. + */ +interface KonanResolvedLibrary: PackageAccessedHandler { + + // The library itself. + val library: KonanLibrary + + // Dependencies on other libraries. + val resolvedDependencies: List + + // Whether it is needed to linker. + val isNeededForLink: Boolean + + // Is provided by the distribution? + val isDefault: Boolean +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt new file mode 100644 index 00000000000..d563413e461 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanLibraryResolverImpl.kt @@ -0,0 +1,142 @@ +/* + * 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.kotlin.konan.library.resolver.impl + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.SearchPathResolverWithTarget +import org.jetbrains.kotlin.konan.library.createKonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.* +import org.jetbrains.kotlin.konan.library.unresolvedDependencies + +internal class KonanLibraryResolverImpl( + override val searchPathResolver: SearchPathResolverWithTarget, + override val abiVersion: Int +) : KonanLibraryResolver { + + override fun resolveWithDependencies( + libraryNames: List, + noStdLib: Boolean, + noDefaultLibs: Boolean, + logger: DuplicatedLibraryLogger? + ) = findLibraries(libraryNames, noStdLib, noDefaultLibs) + .leaveDistinct(logger) + .resolveDependencies() + + /** + * Returns the list of libraries based on [libraryNames], [noStdLib] and [noDefaultLibs] criteria. + * + * This method does not return any libraries that might be available via transitive dependencies + * from the original library set (root set). + */ + private fun findLibraries( + libraryNames: List, + noStdLib: Boolean, + noDefaultLibs: Boolean + ): List { + + val userProvidedLibraries = libraryNames.asSequence() + .map { searchPathResolver.resolve(it) } + .map { createKonanLibrary(it, abiVersion, searchPathResolver.target) } + .toList() + + val defaultLibraries = searchPathResolver.defaultLinks(noStdLib, noDefaultLibs).map { + createKonanLibrary(it, abiVersion, searchPathResolver.target, isDefault = true) + } + + // Make sure the user provided ones appear first, so that + // they have precedence over defaults when duplicates are eliminated. + return userProvidedLibraries + defaultLibraries + } + + /** + * Leaves only distinct libraries (by absolute path), warns on duplicated paths. + */ + private fun List.leaveDistinct(logger: DuplicatedLibraryLogger?) = + this.groupBy { it.libraryFile.absolutePath }.let { groupedByAbsolutePath -> + warnOnLibraryDuplicates(groupedByAbsolutePath.filter { it.value.size > 1 }.keys, logger) + groupedByAbsolutePath.map { it.value.first() } + } + + private fun warnOnLibraryDuplicates(duplicatedPaths: Iterable, logger: DuplicatedLibraryLogger?) { + if (logger == null) return + duplicatedPaths.forEach { logger("library included more than once: $it") } + } + + + /** + * Given the list of root libraries does the following: + * + * 1. Evaluates other libraries that are available via transitive dependencies. + * 2. Wraps each [KonanLibrary] into a [KonanResolvedLibrary] with information about dependencies on other libraries. + * 3. Creates resulting [KonanLibraryResolveResult] object. + */ + private fun List.resolveDependencies(): KonanLibraryResolveResult { + + val rootLibraries = this.map { KonanResolvedLibraryImpl(it) } + + // As far as the list of root libraries is known from the very beginning, the result can be + // constructed from the very beginning as well. + val result = KonanLibraryResolverResultImpl(rootLibraries) + + val cache = mutableMapOf() + cache.putAll(rootLibraries.map { it.library.libraryFile.absoluteFile to it }) + + var newDependencies = rootLibraries + do { + newDependencies = newDependencies.map { library: KonanResolvedLibraryImpl -> + library.library.unresolvedDependencies.asSequence() + .map { searchPathResolver.resolve(it).absoluteFile } + .mapNotNull { + if (it in cache) { + library.addDependency(cache[it]!!) + null + } else { + val newLibrary = KonanResolvedLibraryImpl(createKonanLibrary(it, abiVersion, searchPathResolver.target)) + cache[it] = newLibrary + library.addDependency(newLibrary) + newLibrary + } + } + .toList() + }.flatten() + } while (newDependencies.isNotEmpty()) + + return result + } +} + +internal class KonanLibraryResolverResultImpl( + private val roots: List +) : KonanLibraryResolveResult { + + private val all: List by lazy { + val result = mutableSetOf().also { it.addAll(roots) } + + var newDependencies = result.toList() + do { + newDependencies = newDependencies + .map { it -> it.resolvedDependencies }.flatten() + .filter { it !in result } + result.addAll(newDependencies) + } while (newDependencies.isNotEmpty()) + + result.toList() + } + + override fun filterRoots(predicate: (KonanResolvedLibrary) -> Boolean) = + KonanLibraryResolverResultImpl(roots.filter(predicate)) + + override fun getFullList(order: LibraryOrder?) = (order?.invoke(all) ?: all).asPlain() + + override fun forEach(action: (KonanLibrary, PackageAccessedHandler) -> Unit) { + all.forEach { action(it.library, it) } + } + + private fun List.asPlain() = map { it.library } + + override fun toString() = "roots=$roots, all=$all" +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt new file mode 100644 index 00000000000..3b9042efb04 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/library/resolver/impl/KonanResolvedLibraryImpl.kt @@ -0,0 +1,40 @@ +/* + * 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.kotlin.konan.library.resolver.impl + +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.KonanResolvedLibrary +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.serialization.konan.parseModuleHeader + +internal class KonanResolvedLibraryImpl( + override val library: KonanLibrary +) : KonanResolvedLibrary { + + private val _resolvedDependencies = mutableListOf() + private val _emptyPackages by lazy { parseModuleHeader(library.moduleHeaderData).emptyPackageList } + + override val resolvedDependencies: List + get() = _resolvedDependencies + + internal fun addDependency(resolvedLibrary: KonanResolvedLibrary) = _resolvedDependencies.add(resolvedLibrary) + + override var isNeededForLink: Boolean = false + private set + + override val isDefault: Boolean + get() = library.isDefault + + override fun markPackageAccessed(fqName: FqName) { + if (!isNeededForLink // fast path + && !_emptyPackages.contains(fqName.asString()) + ) { + isNeededForLink = true + } + } + + override fun toString() = "library=$library, dependsOn=${_resolvedDependencies.joinToString { it.library.toString() }}" +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/konan/utils/KonanFactories.kt b/konan/serializer/src/org/jetbrains/kotlin/konan/utils/KonanFactories.kt new file mode 100644 index 00000000000..600940c421b --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/konan/utils/KonanFactories.kt @@ -0,0 +1,56 @@ +/* + * 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.kotlin.konan.utils + +import org.jetbrains.kotlin.descriptors.konan.KonanModuleDescriptorFactory +import org.jetbrains.kotlin.descriptors.konan.impl.KonanModuleDescriptorFactoryImpl +import org.jetbrains.kotlin.serialization.konan.KonanDeserializedModuleDescriptorFactory +import org.jetbrains.kotlin.serialization.konan.KonanDeserializedPackageFragmentsFactory +import org.jetbrains.kotlin.serialization.konan.KonanResolvedModuleDescriptorsFactory +import org.jetbrains.kotlin.serialization.konan.impl.KonanDeserializedModuleDescriptorFactoryImpl +import org.jetbrains.kotlin.serialization.konan.impl.KonanDeserializedPackageFragmentsFactoryImpl +import org.jetbrains.kotlin.serialization.konan.impl.KonanResolvedModuleDescriptorsFactoryImpl + +/** + * The default Kotlin/Native factories. + */ +object KonanFactories { + + /** + * The default [KonanModuleDescriptorFactory] factory instance. + */ + val DefaultDescriptorFactory: KonanModuleDescriptorFactory = KonanModuleDescriptorFactoryImpl + + /** + * The default [KonanDeserializedPackageFragmentsFactory] factory instance. + */ + val DefaultPackageFragmentsFactory: KonanDeserializedPackageFragmentsFactory = + KonanDeserializedPackageFragmentsFactoryImpl + + /** + * The default [KonanDeserializedModuleDescriptorFactory] factory instance. + */ + val DefaultDeserializedDescriptorFactory: KonanDeserializedModuleDescriptorFactory = + createDefaultKonanDeserializedModuleDescriptorFactory( + DefaultDescriptorFactory, DefaultPackageFragmentsFactory + ) + + /** + * The default [KonanResolvedModuleDescriptorsFactory] factory instance. + */ + val DefaultResolvedDescriptorsFactory: KonanResolvedModuleDescriptorsFactory = + createDefaultKonanResolvedModuleDescriptorsFactory(DefaultDeserializedDescriptorFactory) + + fun createDefaultKonanDeserializedModuleDescriptorFactory( + descriptorFactory: KonanModuleDescriptorFactory, + packageFragmentsFactory: KonanDeserializedPackageFragmentsFactory + ): KonanDeserializedModuleDescriptorFactory = + KonanDeserializedModuleDescriptorFactoryImpl(descriptorFactory, packageFragmentsFactory) + + fun createDefaultKonanResolvedModuleDescriptorsFactory( + moduleDescriptorFactory: KonanDeserializedModuleDescriptorFactory + ): KonanResolvedModuleDescriptorsFactory = KonanResolvedModuleDescriptorsFactoryImpl(moduleDescriptorFactory) +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatform.kt b/konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatform.kt new file mode 100644 index 00000000000..94f39984208 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatform.kt @@ -0,0 +1,22 @@ +/* + * 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.kotlin.resolve.konan.platform + +import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.resolve.MultiTargetPlatform +import org.jetbrains.kotlin.resolve.PlatformConfigurator +import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.storage.StorageManager + +object KonanPlatform : TargetPlatform("Konan") { + + override fun computePlatformSpecificDefaultImports(storageManager: StorageManager, result: MutableList) { + result.add(ImportPath.fromString("kotlin.native.*")) + } + + override val multiTargetPlatform = MultiTargetPlatform.Specific(platformName) + override val platformConfigurator: PlatformConfigurator = KonanPlatformConfigurator +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatformConfigurator.kt b/konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatformConfigurator.kt new file mode 100644 index 00000000000..8f8125e52f1 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/resolve/konan/platform/KonanPlatformConfigurator.kt @@ -0,0 +1,42 @@ +/* + * 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.kotlin.resolve.konan.platform + +import org.jetbrains.kotlin.builtins.PlatformToKotlinClassMap +import org.jetbrains.kotlin.container.StorageComponentContainer +import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.calls.checkers.ReifiedTypeParameterSubstitutionChecker +import org.jetbrains.kotlin.resolve.calls.components.SamConversionTransformer +import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker +import org.jetbrains.kotlin.resolve.lazy.DelegationFilter +import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes +import org.jetbrains.kotlin.types.DynamicTypesSettings + +object KonanPlatformConfigurator : PlatformConfigurator( + DynamicTypesSettings(), + additionalDeclarationCheckers = listOf(ExpectedActualDeclarationChecker), + additionalCallCheckers = listOf( + org.jetbrains.kotlin.resolve.jvm.checkers.SuperCallWithDefaultArgumentsChecker(), + ReifiedTypeParameterSubstitutionChecker() + ), + additionalTypeCheckers = listOf(), + additionalClassifierUsageCheckers = listOf(), + additionalAnnotationCheckers = listOf(), + identifierChecker = IdentifierChecker.Default, + overloadFilter = OverloadFilter.Default, + platformToKotlinClassMap = PlatformToKotlinClassMap.EMPTY, + delegationFilter = DelegationFilter.Default, + overridesBackwardCompatibilityHelper = OverridesBackwardCompatibilityHelper.Default, + declarationReturnTypeSanitizer = DeclarationReturnTypeSanitizer.Default +) { + override fun configureModuleComponents(container: StorageComponentContainer) { + container.useInstance(SyntheticScopes.Empty) + container.useInstance(TypeSpecificityComparator.NONE) + container.useInstance(SamConversionTransformer.Empty) + } +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt new file mode 100644 index 00000000000..2d5ea1a4464 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt @@ -0,0 +1,34 @@ +/* + * 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.kotlin.serialization.konan + +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 + +class KonanClassDataFinder( + private val fragment: KonanProtoBuf.LinkDataPackageFragment, + private val nameResolver: NameResolver +) : ClassDataFinder { + + override fun findClassData(classId: ClassId): ClassData? { + val proto = fragment.classes + val nameList = proto.classNameList + + val index = nameList.indexOfFirst { nameResolver.getClassId(it) == classId } + if (index == -1) + error("Could not find serialized class $classId") + + val foundClass = proto.getClasses(index) ?: error("Could not find data for serialized class $classId") + + /* TODO: binary version supposed to be read from protobuf. */ + return ClassData(nameResolver, foundClass, KonanMetadataVersion.INSTANCE, SourceElement.NO_SOURCE) + } +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializationUtils.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializationUtils.kt new file mode 100644 index 00000000000..35a98d2fe5c --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializationUtils.kt @@ -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.kotlin.serialization.konan + +import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf + +fun parsePackageFragment(packageMetadata: ByteArray): KonanProtoBuf.LinkDataPackageFragment = + KonanProtoBuf.LinkDataPackageFragment.parseFrom(packageMetadata, KonanSerializerProtocol.extensionRegistry) + +fun parseModuleHeader(libraryMetadata: ByteArray): KonanProtoBuf.LinkDataLibrary = + KonanProtoBuf.LinkDataLibrary.parseFrom(libraryMetadata, KonanSerializerProtocol.extensionRegistry) diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedModuleDescriptorFactory.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedModuleDescriptorFactory.kt new file mode 100644 index 00000000000..6c006c1ab11 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedModuleDescriptorFactory.kt @@ -0,0 +1,40 @@ +/* + * 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.kotlin.serialization.konan + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.konan.KonanModuleDescriptorFactory +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.PackageAccessedHandler +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.storage.StorageManager + +interface KonanDeserializedModuleDescriptorFactory { + + val descriptorFactory: KonanModuleDescriptorFactory + val packageFragmentsFactory: KonanDeserializedPackageFragmentsFactory + + fun createDescriptor( + library: KonanLibrary, + languageVersionSettings: LanguageVersionSettings, + storageManager: StorageManager, + builtIns: KotlinBuiltIns, + packageAccessedHandler: PackageAccessedHandler? = null + ): ModuleDescriptorImpl + + /** + * Please use this method with care: As far as it creates an instance of [KotlinBuiltIns] it should be + * normally used for creation of the very first (e.g. "stdlib") module in the set of created modules. + */ + fun createDescriptorAndNewBuiltIns( + library: KonanLibrary, + languageVersionSettings: LanguageVersionSettings, + storageManager: StorageManager, + packageAccessedHandler: PackageAccessedHandler? = null + ): ModuleDescriptorImpl +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedPackageFragmentsFactory.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedPackageFragmentsFactory.kt new file mode 100644 index 00000000000..d85b7c24b66 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanDeserializedPackageFragmentsFactory.kt @@ -0,0 +1,29 @@ +/* + * 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.kotlin.serialization.konan + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.PackageAccessedHandler +import org.jetbrains.kotlin.storage.StorageManager + +interface KonanDeserializedPackageFragmentsFactory { + + fun createDeserializedPackageFragments( + library: KonanLibrary, + packageFragmentNames: List, + moduleDescriptor: ModuleDescriptor, + packageAccessedHandler: PackageAccessedHandler?, + storageManager: StorageManager + ): List + + fun createSyntheticPackageFragments( + library: KonanLibrary, + deserializedPackageFragments: List, + moduleDescriptor: ModuleDescriptor + ): List +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanMetadataVersion.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanMetadataVersion.kt new file mode 100644 index 00000000000..631908c874f --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanMetadataVersion.kt @@ -0,0 +1,21 @@ +/* + * 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.kotlin.serialization.konan + +import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion + +class KonanMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) { + + override fun isCompatible(): Boolean = this.major == 1 && this.minor == 0 + + companion object { + @JvmField + val INSTANCE = KonanMetadataVersion(1, 0, 0) + + @JvmField + val INVALID_VERSION = KonanMetadataVersion() + } +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt new file mode 100644 index 00000000000..ec0813b96a5 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt @@ -0,0 +1,88 @@ +/* + * 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.kotlin.serialization.konan + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.PackageAccessedHandler +import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl +import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.deserialization.DeserializationComponents +import org.jetbrains.kotlin.serialization.deserialization.DeserializedPackageFragment +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPackageMemberScope +import org.jetbrains.kotlin.serialization.deserialization.getClassId +import org.jetbrains.kotlin.serialization.deserialization.getName +import org.jetbrains.kotlin.storage.StorageManager + +class KonanPackageFragment( + fqName: FqName, + private val library: KonanLibrary, + private val packageAccessedHandler: PackageAccessedHandler?, + storageManager: StorageManager, + module: ModuleDescriptor +) : DeserializedPackageFragment(fqName, storageManager, module) { + + lateinit var components: DeserializationComponents + + override fun initialize(components: DeserializationComponents) { + this.components = components + } + + // The proto field is lazy so that we can load only needed + // packages from the library. + private val protoForNames: KonanProtoBuf.LinkDataPackageFragment by lazy { + parsePackageFragment(library.packageMetadata(fqName.asString())) + } + + val proto: KonanProtoBuf.LinkDataPackageFragment + get() = protoForNames.also { packageAccessedHandler?.markPackageAccessed(fqName) } + + private val nameResolver by lazy { + NameResolverImpl(protoForNames.stringTable, protoForNames.nameTable) + } + + override val classDataFinder by lazy { + KonanClassDataFinder(proto, nameResolver) + } + + private val _memberScope by lazy { + /* TODO: we fake proto binary versioning for now. */ + DeserializedPackageMemberScope( + this, + proto.getPackage(), + nameResolver, + KonanMetadataVersion.INSTANCE, + /* containerSource = */ null, + components + ) { loadClassNames() } + } + + override fun getMemberScope(): DeserializedPackageMemberScope = _memberScope + + private val classifierNames: Set by lazy { + val result = mutableSetOf() + result.addAll(loadClassNames()) + protoForNames.getPackage().typeAliasList.mapTo(result) { nameResolver.getName(it.name) } + result + } + + fun hasTopLevelClassifier(name: Name): Boolean = name in classifierNames + + private fun loadClassNames(): Collection { + + val classNameList = protoForNames.classes.classNameList + + val names = classNameList.mapNotNull { + val classId = nameResolver.getClassId(it) + val shortName = classId.shortClassName + if (!classId.isNestedClass) shortName else null + } + + return names + } +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanResolvedModuleDescriptorsFactory.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanResolvedModuleDescriptorsFactory.kt new file mode 100644 index 00000000000..0bfd417c20a --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanResolvedModuleDescriptorsFactory.kt @@ -0,0 +1,54 @@ +/* + * 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.kotlin.serialization.konan + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolveResult +import org.jetbrains.kotlin.storage.StorageManager + +interface KonanResolvedModuleDescriptorsFactory { + + val moduleDescriptorFactory: KonanDeserializedModuleDescriptorFactory + + /** + * Given the [resolvedLibraries] creates the list of [ModuleDescriptorImpl]s with properly installed + * inter-dependencies. The result of this method is returned in a form of [KonanResolvedModuleDescriptors] instance. + * + * Please use this method with care: Unless this method accepts `null` for [builtIns], it is not recommended to + * invoke it this way. If you are compiling a source module, please supply the non-null [builtIns] from the + * source module, so that all modules created in your compilation session will share the same built-ins instance. + * + * Otherwise (if `null` was supplied), a new instance of [KotlinBuiltIns] will be created. The created built-ins + * instance will be shared by all modules created in this method. But this instance will have no connection + * with probably existing built-ins instance of your source module(s). + */ + fun createResolved( + resolvedLibraries: KonanLibraryResolveResult, + storageManager: StorageManager, + builtIns: KotlinBuiltIns?, + languageVersionSettings: LanguageVersionSettings, + customAction: ((KonanLibrary, ModuleDescriptorImpl) -> Unit)? = null + ): KonanResolvedModuleDescriptors +} + +class KonanResolvedModuleDescriptors( + + /** + * The list of modules each representing an individual Kotlin/Native library. All modules + * in this list have properly installed dependencies, i.e. module has all necessary dependencies + * on other modules plus a dependency on the [forwardDeclarationsModule]. + */ + val resolvedDescriptors: List, + + /** + * This is a module which "contains" forward declarations. + * Note: this module should be unique per compilation and should always be the last dependency of any module. + */ + val forwardDeclarationsModule: ModuleDescriptorImpl +) diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanSerializerProtocol.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanSerializerProtocol.kt new file mode 100644 index 00000000000..9ed595e3b32 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/KonanSerializerProtocol.kt @@ -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.kotlin.serialization.konan + +import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf +import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite +import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol + +object KonanSerializerProtocol : SerializerExtensionProtocol( + ExtensionRegistryLite.newInstance().apply { KonanProtoBuf.registerAllExtensions(this) }, + KonanProtoBuf.packageFqName, + KonanProtoBuf.constructorAnnotation, + KonanProtoBuf.classAnnotation, + KonanProtoBuf.functionAnnotation, + KonanProtoBuf.propertyAnnotation, + KonanProtoBuf.enumEntryAnnotation, + KonanProtoBuf.compileTimeValue, + KonanProtoBuf.parameterAnnotation, + KonanProtoBuf.typeAnnotation, + KonanProtoBuf.typeParameterAnnotation +) diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/NullFlexibleTypeDeserializer.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/NullFlexibleTypeDeserializer.kt new file mode 100644 index 00000000000..0d64afdf067 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/NullFlexibleTypeDeserializer.kt @@ -0,0 +1,20 @@ +/* + * 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.kotlin.serialization.konan + +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer +import org.jetbrains.kotlin.types.SimpleType + +object NullFlexibleTypeDeserializer : FlexibleTypeDeserializer { + + override fun create( + proto: ProtoBuf.Type, + flexibleId: String, + lowerBound: SimpleType, + upperBound: SimpleType + ) = error("Illegal use of flexible type deserializer.") +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedModuleDescriptorFactoryImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedModuleDescriptorFactoryImpl.kt new file mode 100644 index 00000000000..f6aed45eb64 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedModuleDescriptorFactoryImpl.kt @@ -0,0 +1,130 @@ +/* + * 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.kotlin.serialization.konan.impl + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.NotFoundClasses +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.PackageFragmentProviderImpl +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.konan.DeserializedKonanModuleOrigin +import org.jetbrains.kotlin.descriptors.konan.KonanModuleDescriptorFactory +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.PackageAccessedHandler +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration +import org.jetbrains.kotlin.serialization.deserialization.* +import org.jetbrains.kotlin.serialization.konan.* +import org.jetbrains.kotlin.storage.StorageManager + +internal class KonanDeserializedModuleDescriptorFactoryImpl( + override val descriptorFactory: KonanModuleDescriptorFactory, + override val packageFragmentsFactory: KonanDeserializedPackageFragmentsFactory +) : KonanDeserializedModuleDescriptorFactory { + + override fun createDescriptor( + library: KonanLibrary, + languageVersionSettings: LanguageVersionSettings, + storageManager: StorageManager, + builtIns: KotlinBuiltIns, + packageAccessedHandler: PackageAccessedHandler? + ) = createDescriptorOptionalBuiltIns(library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler) + + override fun createDescriptorAndNewBuiltIns( + library: KonanLibrary, + languageVersionSettings: LanguageVersionSettings, + storageManager: StorageManager, + packageAccessedHandler: PackageAccessedHandler? + ) = createDescriptorOptionalBuiltIns(library, languageVersionSettings, storageManager, null, packageAccessedHandler) + + private fun createDescriptorOptionalBuiltIns( + library: KonanLibrary, + languageVersionSettings: LanguageVersionSettings, + storageManager: StorageManager, + builtIns: KotlinBuiltIns?, + packageAccessedHandler: PackageAccessedHandler? + ): ModuleDescriptorImpl { + + val libraryProto = parseModuleHeader(library.moduleHeaderData) + + val moduleName = Name.special(libraryProto.moduleName) + val moduleOrigin = DeserializedKonanModuleOrigin(library) + + val moduleDescriptor = if (builtIns != null) + descriptorFactory.createDescriptor(moduleName, storageManager, builtIns, moduleOrigin) + else + descriptorFactory.createDescriptorAndNewBuiltIns(moduleName, storageManager, moduleOrigin) + + val deserializationConfiguration = CompilerDeserializationConfiguration(languageVersionSettings) + + val provider = createPackageFragmentProvider( + library, + packageAccessedHandler, + libraryProto.packageFragmentNameList, + storageManager, + moduleDescriptor, + deserializationConfiguration + ) + + moduleDescriptor.initialize(provider) + + return moduleDescriptor + } + + private fun createPackageFragmentProvider( + library: KonanLibrary, + packageAccessedHandler: PackageAccessedHandler?, + packageFragmentNames: List, + storageManager: StorageManager, + moduleDescriptor: ModuleDescriptor, + configuration: DeserializationConfiguration + ): PackageFragmentProvider { + + val deserializedPackageFragments = packageFragmentsFactory.createDeserializedPackageFragments( + library, packageFragmentNames, moduleDescriptor, packageAccessedHandler, storageManager + ) + + val syntheticPackageFragments = packageFragmentsFactory.createSyntheticPackageFragments( + library, deserializedPackageFragments, moduleDescriptor + ) + + val provider = PackageFragmentProviderImpl(deserializedPackageFragments + syntheticPackageFragments) + + val notFoundClasses = NotFoundClasses(storageManager, moduleDescriptor) + + val annotationAndConstantLoader = AnnotationAndConstantLoaderImpl( + moduleDescriptor, + notFoundClasses, + KonanSerializerProtocol + ) + + val components = DeserializationComponents( + storageManager, + moduleDescriptor, + configuration, + DeserializedClassDataFinder(provider), + annotationAndConstantLoader, + provider, + LocalClassifierTypeSettings.Default, + ErrorReporter.DO_NOTHING, + LookupTracker.DO_NOTHING, + NullFlexibleTypeDeserializer, + emptyList(), + notFoundClasses, + ContractDeserializer.DEFAULT, + extensionRegistryLite = KonanSerializerProtocol.extensionRegistry + ) + + for (packageFragment in deserializedPackageFragments) { + packageFragment.initialize(components) + } + + return provider + } +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt new file mode 100644 index 00000000000..a21518f008f --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt @@ -0,0 +1,138 @@ +/* + * 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.kotlin.serialization.konan.impl + +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.exportForwardDeclarations +import org.jetbrains.kotlin.konan.library.isInterop +import org.jetbrains.kotlin.konan.library.packageFqName +import org.jetbrains.kotlin.konan.library.resolver.PackageAccessedHandler +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl +import org.jetbrains.kotlin.serialization.konan.KonanDeserializedPackageFragmentsFactory +import org.jetbrains.kotlin.serialization.konan.KonanPackageFragment +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.utils.Printer +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult + +// FIXME(ddol): decouple and move interop-specific logic back to Kotlin/Native. +internal object KonanDeserializedPackageFragmentsFactoryImpl : KonanDeserializedPackageFragmentsFactory { + + override fun createDeserializedPackageFragments( + library: KonanLibrary, + packageFragmentNames: List, + moduleDescriptor: ModuleDescriptor, + packageAccessedHandler: PackageAccessedHandler?, + storageManager: StorageManager + ) = packageFragmentNames.map { + KonanPackageFragment(FqName(it), library, packageAccessedHandler, storageManager, moduleDescriptor) + } + + override fun createSyntheticPackageFragments( + library: KonanLibrary, + deserializedPackageFragments: List, + moduleDescriptor: ModuleDescriptor + ): List { + + if (!library.isInterop) return emptyList() + + val mainPackageFqName = library.packageFqName + ?: error("Inconsistent manifest: interop library ${library.libraryName} should have `package` specified") + val exportForwardDeclarations = library.exportForwardDeclarations + + val aliasedPackageFragments = deserializedPackageFragments.filter { it.fqName == mainPackageFqName } + + val result = mutableListOf() + listOf( + ForwardDeclarationsFqNames.cNamesStructs, + ForwardDeclarationsFqNames.objCNamesClasses, + ForwardDeclarationsFqNames.objCNamesProtocols + ).mapTo(result) { fqName -> + ClassifierAliasingPackageFragmentDescriptor(aliasedPackageFragments, moduleDescriptor, fqName) + } + + result.add(ExportedForwardDeclarationsPackageFragmentDescriptor(moduleDescriptor, mainPackageFqName, exportForwardDeclarations)) + + return result + } +} + +/** + * The package fragment to export forward declarations from interop package namespace, i.e. + * redirect "$pkg.$name" to e.g. "cnames.structs.$name". + */ +class ExportedForwardDeclarationsPackageFragmentDescriptor( + module: ModuleDescriptor, + fqName: FqName, + declarations: List +) : PackageFragmentDescriptorImpl(module, fqName) { + + private val memberScope = object : MemberScopeImpl() { + + private val nameToFqName = declarations.map { it.shortName() to it }.toMap() + + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? { + val declFqName = nameToFqName[name] ?: return null + + val packageView = module.getPackage(declFqName.parent()) + return packageView.memberScope.getContributedClassifier(name, location) + } + + override fun printScopeStructure(p: Printer) { + p.println(this::class.java.simpleName, " {") + p.pushIndent() + + p.println("declarations = $declarations") + + p.popIndent() + p.println("}") + } + + } + + override fun getMemberScope() = memberScope +} + +/** + * The package fragment that redirects all requests for classifier lookup to its targets. + */ +class ClassifierAliasingPackageFragmentDescriptor( + targets: List, + module: ModuleDescriptor, + fqName: FqName +) : PackageFragmentDescriptorImpl(module, fqName) { + + private val memberScope = object : MemberScopeImpl() { + + override fun getContributedClassifier(name: Name, location: LookupLocation) = + targets.firstNotNullResult { + if (it.hasTopLevelClassifier(name)) { + it.getMemberScope().getContributedClassifier(name, location) + } else { + null + } + } + + override fun printScopeStructure(p: Printer) { + p.println(this::class.java.simpleName, " {") + p.pushIndent() + + p.println("targets = $targets") + + p.popIndent() + p.println("}") + } + } + + override fun getMemberScope(): MemberScope = memberScope +} diff --git a/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanResolvedModuleDescriptorsFactoryImpl.kt b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanResolvedModuleDescriptorsFactoryImpl.kt new file mode 100644 index 00000000000..aee43936170 --- /dev/null +++ b/konan/serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanResolvedModuleDescriptorsFactoryImpl.kt @@ -0,0 +1,189 @@ +/* + * 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.kotlin.serialization.konan.impl + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl +import org.jetbrains.kotlin.descriptors.konan.KonanModuleOrigin +import org.jetbrains.kotlin.descriptors.konan.SyntheticModulesOrigin +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.konan.library.KonanLibrary +import org.jetbrains.kotlin.konan.library.resolver.KonanLibraryResolveResult +import org.jetbrains.kotlin.konan.library.resolver.PackageAccessedHandler +import org.jetbrains.kotlin.konan.util.profile +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl +import org.jetbrains.kotlin.serialization.konan.KonanDeserializedModuleDescriptorFactory +import org.jetbrains.kotlin.serialization.konan.KonanResolvedModuleDescriptors +import org.jetbrains.kotlin.serialization.konan.KonanResolvedModuleDescriptorsFactory +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.storage.getValue +import org.jetbrains.kotlin.utils.Printer + +class KonanResolvedModuleDescriptorsFactoryImpl( + override val moduleDescriptorFactory: KonanDeserializedModuleDescriptorFactory +) : KonanResolvedModuleDescriptorsFactory { + + override fun createResolved( + resolvedLibraries: KonanLibraryResolveResult, + storageManager: StorageManager, + builtIns: KotlinBuiltIns?, + languageVersionSettings: LanguageVersionSettings, + customAction: ((KonanLibrary, ModuleDescriptorImpl) -> Unit)? + ): KonanResolvedModuleDescriptors { + + val moduleDescriptors = mutableListOf() + @Suppress("NAME_SHADOWING") + var builtIns = builtIns + + // Build module descriptors. + resolvedLibraries.forEach { library, packageAccessedHandler -> + profile("Loading ${library.libraryName}") { + + // MutableModuleContext needs ModuleDescriptorImpl, rather than ModuleDescriptor. + val moduleDescriptor = createDescriptorOptionalBuiltsIns( + library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler + ) + builtIns = moduleDescriptor.builtIns + moduleDescriptors.add(moduleDescriptor) + + customAction?.invoke(library, moduleDescriptor) + } + } + + val forwardDeclarationsModule = createForwardDeclarationsModule(builtIns, storageManager) + + // Set inter-dependencies between module descriptors, add forwarding declarations module. + for (module in moduleDescriptors) { + // Yes, just to all of them. + module.setDependencies(moduleDescriptors + forwardDeclarationsModule) + } + + return KonanResolvedModuleDescriptors(moduleDescriptors, forwardDeclarationsModule) + } + + private fun createForwardDeclarationsModule( + builtIns: KotlinBuiltIns?, + storageManager: StorageManager + ): ModuleDescriptorImpl { + + val name = Name.special("") + val module = createDescriptorOptionalBuiltsIns(name, storageManager, builtIns, SyntheticModulesOrigin) + + fun createPackage(fqName: FqName, supertypeName: String, classKind: ClassKind) = + ForwardDeclarationsPackageFragmentDescriptor( + storageManager, + module, + fqName, + Name.identifier(supertypeName), + classKind + ) + + val packageFragmentProvider = PackageFragmentProviderImpl( + listOf( + createPackage(ForwardDeclarationsFqNames.cNamesStructs, "COpaque", ClassKind.CLASS), + createPackage(ForwardDeclarationsFqNames.objCNamesClasses, "ObjCObjectBase", ClassKind.CLASS), + createPackage(ForwardDeclarationsFqNames.objCNamesProtocols, "ObjCObject", ClassKind.INTERFACE) + ) + ) + + module.initialize(packageFragmentProvider) + module.setDependencies(module) + + return module + } + + private fun createDescriptorOptionalBuiltsIns( + name: Name, + storageManager: StorageManager, + builtIns: KotlinBuiltIns?, + moduleOrigin: KonanModuleOrigin + ) = if (builtIns != null) + moduleDescriptorFactory.descriptorFactory.createDescriptor(name, storageManager, builtIns, moduleOrigin) + else + moduleDescriptorFactory.descriptorFactory.createDescriptorAndNewBuiltIns(name, storageManager, moduleOrigin) + + private fun createDescriptorOptionalBuiltsIns( + library: KonanLibrary, + languageVersionSettings: LanguageVersionSettings, + storageManager: StorageManager, + builtIns: KotlinBuiltIns?, + packageAccessedHandler: PackageAccessedHandler? + ) = if (builtIns != null) + moduleDescriptorFactory.createDescriptor(library, languageVersionSettings, storageManager, builtIns, packageAccessedHandler) + else + moduleDescriptorFactory.createDescriptorAndNewBuiltIns(library, languageVersionSettings, storageManager, packageAccessedHandler) +} + +/** + * Package fragment which creates descriptors for forward declarations on demand. + */ +private class ForwardDeclarationsPackageFragmentDescriptor( + storageManager: StorageManager, + module: ModuleDescriptor, + fqName: FqName, + supertypeName: Name, + classKind: ClassKind +) : PackageFragmentDescriptorImpl(module, fqName) { + + private val memberScope = object : MemberScopeImpl() { + + private val declarations = storageManager.createMemoizedFunction(this::createDeclaration) + + private val supertype by storageManager.createLazyValue { + val descriptor = builtIns.builtInsModule.getPackage(ForwardDeclarationsFqNames.packageName) + .memberScope + .getContributedClassifier(supertypeName, NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + descriptor.defaultType + } + + private fun createDeclaration(name: Name): ClassDescriptor { + return ClassDescriptorImpl( + this@ForwardDeclarationsPackageFragmentDescriptor, + name, + Modality.FINAL, + classKind, + listOf(supertype), + SourceElement.NO_SOURCE, + false, + LockBasedStorageManager.NO_LOCKS + ).apply { + this.initialize(MemberScope.Empty, emptySet(), null) + } + } + + override fun getContributedClassifier(name: Name, location: LookupLocation) = declarations(name) + + override fun printScopeStructure(p: Printer) { + p.println(this::class.java.simpleName, "{}") + } + } + + override fun getMemberScope(): MemberScope = memberScope +} + +// FIXME(ddol): decouple and move interop-specific logic back to Kotlin/Native. +internal object ForwardDeclarationsFqNames { + + val packageName = FqName("kotlinx.cinterop") + + val cNames = FqName("cnames") + val cNamesStructs = cNames.child(Name.identifier("structs")) + + val objCNames = FqName("objcnames") + val objCNamesClasses = objCNames.child(Name.identifier("classes")) + val objCNamesProtocols = objCNames.child(Name.identifier("protocols")) +} diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/file/File.kt b/konan/utils/src/org/jetbrains/kotlin/konan/file/File.kt new file mode 100644 index 00000000000..8f7b4a74f27 --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/file/File.kt @@ -0,0 +1,245 @@ +/* + * 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.kotlin.konan.file + +import java.io.BufferedReader +import java.io.InputStream +import java.io.InputStreamReader +import java.net.URI +import java.nio.file.* +import java.nio.file.attribute.BasicFileAttributes + +data class File constructor(internal val javaPath: Path) { + constructor(parent: Path, child: String) : this(parent.resolve(child)) + constructor(parent: File, child: String) : this(parent.javaPath.resolve(child)) + constructor(path: String) : this(Paths.get(path)) + constructor(parent: String, child: String) : this(Paths.get(parent, child)) + + val path: String + get() = javaPath.toString() + val absolutePath: String + get() = javaPath.toAbsolutePath().toString() + val absoluteFile: File + get() = File(absolutePath) + val name: String + get() = javaPath.fileName.toString() + val extension: String + get() = name.substringAfterLast('.', "") + val parent: String + get() = javaPath.parent.toString() + val parentFile: File + get() = File(javaPath.parent) + + val exists + get() = Files.exists(javaPath) + val isDirectory + get() = Files.isDirectory(javaPath) + val isFile + get() = Files.isRegularFile(javaPath) + val isAbsolute + get() = javaPath.isAbsolute() + val listFiles: List + get() = Files.newDirectoryStream(javaPath).use { stream -> stream.map { File(it) } } + val listFilesOrEmpty: List + get() = if (exists) listFiles else emptyList() + + fun child(name: String) = File(this, name) + + fun copyTo(destination: File) { + Files.copy(javaPath, destination.javaPath, StandardCopyOption.REPLACE_EXISTING) + } + + fun recursiveCopyTo(destination: File) { + val sourcePath = javaPath + val destPath = destination.javaPath + sourcePath.recursiveCopyTo(destPath) + } + + fun mkdirs() = Files.createDirectories(javaPath) + fun delete() = Files.deleteIfExists(javaPath) + fun deleteRecursively() = postorder { Files.delete(it) } + fun deleteOnExitRecursively() = preorder { File(it).deleteOnExit() } + + fun preorder(task: (Path) -> Unit) { + if (!this.exists) return + + Files.walkFileTree(javaPath, object : SimpleFileVisitor() { + override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { + task(file!!) + return FileVisitResult.CONTINUE + } + + override fun preVisitDirectory(dir: Path?, attrs: BasicFileAttributes?): FileVisitResult { + task(dir!!) + return FileVisitResult.CONTINUE + } + }) + + } + + fun postorder(task: (Path) -> Unit) { + if (!this.exists) return + + Files.walkFileTree(javaPath, object : SimpleFileVisitor() { + override fun visitFile(file: Path?, attrs: BasicFileAttributes?): FileVisitResult { + task(file!!) + return FileVisitResult.CONTINUE + } + + override fun postVisitDirectory(dir: Path?, exc: java.io.IOException?): FileVisitResult { + task(dir!!) + return FileVisitResult.CONTINUE + } + }) + + } + + fun deleteOnExit(): File { + // Works only on the default file system, + // but that's okay for now. + javaPath.toFile().deleteOnExit() + return this // Allow streaming. + } + + fun readBytes() = Files.readAllBytes(javaPath) + fun writeBytes(bytes: ByteArray) = Files.write(javaPath, bytes) + fun appendBytes(bytes: ByteArray) = Files.write(javaPath, bytes, StandardOpenOption.APPEND) + + fun writeLines(lines: Iterable) { + Files.write(javaPath, lines) + } + + fun writeText(text: String): Unit = writeLines(listOf(text)) + + fun forEachLine(action: (String) -> Unit) { + Files.lines(javaPath).use { lines -> + lines.forEach { action(it) } + } + } + + fun createAsSymlink(target: String) { + val targetPath = Paths.get(target) + if (Files.isSymbolicLink(this.javaPath) && Files.readSymbolicLink(javaPath) == targetPath) { + return + } + Files.createSymbolicLink(this.javaPath, targetPath) + } + + override fun toString() = path + + // TODO: Consider removeing these after konanazing java.util.Properties. + fun bufferedReader() = Files.newBufferedReader(javaPath) + + fun outputStream() = Files.newOutputStream(javaPath) + fun printWriter() = javaPath.toFile().printWriter() + + companion object { + val userDir + get() = File(System.getProperty("user.dir")) + + val userHome + get() = File(System.getProperty("user.home")) + + val javaHome + get() = File(System.getProperty("java.home")) + val pathSeparator = java.io.File.pathSeparator + } + + fun readStrings() = mutableListOf().also { list -> forEachLine { list.add(it) } } + + override fun equals(other: Any?): Boolean { + val otherFile = other as? File ?: return false + return otherFile.javaPath.toAbsolutePath() == javaPath.toAbsolutePath() + } + + override fun hashCode() = javaPath.toAbsolutePath().hashCode() +} + +fun String.File(): File = File(this) +fun Path.File(): File = File(this) + +fun createTempFile(name: String, suffix: String? = null) = Files.createTempFile(name, suffix).File() +fun createTempDir(name: String): File = Files.createTempDirectory(name).File() + +private val File.zipUri: URI + get() = URI.create("jar:${this.toPath().toUri()}") + +fun File.zipFileSystem(mutable: Boolean = false): FileSystem { + val zipUri = this.zipUri + val attributes = hashMapOf("create" to mutable.toString()) + return try { + FileSystems.newFileSystem(zipUri, attributes, null) + } catch (e: FileSystemAlreadyExistsException) { + FileSystems.getFileSystem(zipUri) + } +} + +fun File.mutableZipFileSystem() = this.zipFileSystem(mutable = true) + +fun File.zipPath(path: String): Path = this.zipFileSystem().getPath(path) + +val File.asZipRoot: File + get() = File(this.zipPath("/")) + +val File.asWritableZipRoot: File + get() = File(this.mutableZipFileSystem().getPath("/")) + +private fun File.toPath() = Paths.get(this.path) + +fun File.zipDirAs(unixFile: File) { + val zipRoot = unixFile.asWritableZipRoot + this.recursiveCopyTo(zipRoot) + zipRoot.javaPath.fileSystem.close() +} + +fun Path.recursiveCopyTo(destPath: Path) { + val sourcePath = this + Files.walk(sourcePath).forEach next@{ oldPath -> + + val relative = sourcePath.relativize(oldPath) + val destFs = destPath.getFileSystem() + // We are copying files between file systems, + // so pass the relative path through the String. + val newPath = destFs.getPath(destPath.toString(), relative.toString()) + + // File systems don't allow replacing an existing root. + if (newPath == newPath.getRoot()) return@next + if (Files.isDirectory(newPath)) { + Files.createDirectories(newPath) + } else { + Files.copy(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING) + } + } +} + +fun bufferedReader(errorStream: InputStream?) = BufferedReader(InputStreamReader(errorStream)) + +// stdlib `use` function adapted for AutoCloseable. +inline fun T.use(block: (T) -> R): R { + var closed = false + try { + return block(this) + } catch (e: Exception) { + closed = true + try { + this?.close() + } catch (closeException: Exception) { + } + throw e + } finally { + if (!closed) { + this?.close() + } + } +} + +fun Path.unzipTo(directory: Path) { + val zipUri = URI.create("jar:" + this.toUri()) + FileSystems.newFileSystem(zipUri, emptyMap(), null).use { zipfs -> + val zipPath = zipfs.getPath("/") + zipPath.recursiveCopyTo(directory) + } +} diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt b/konan/utils/src/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt new file mode 100644 index 00000000000..aa9eee12250 --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/library/SearchPathResolver.kt @@ -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.kotlin.konan.library + +import org.jetbrains.kotlin.konan.file.File +import org.jetbrains.kotlin.konan.target.KonanTarget + +const val KLIB_FILE_EXTENSION = "klib" +const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION" + +const val KONAN_STDLIB_NAME = "stdlib" + +interface SearchPathResolver { + val searchRoots: List + fun resolve(givenPath: String): File + fun defaultLinks(noStdLib: Boolean, noDefaultLibs: Boolean): List +} + +interface SearchPathResolverWithTarget: SearchPathResolver { + val target: KonanTarget +} diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/properties/Properties.kt b/konan/utils/src/org/jetbrains/kotlin/konan/properties/Properties.kt new file mode 100644 index 00000000000..af8de418729 --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/properties/Properties.kt @@ -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.kotlin.konan.properties + +import org.jetbrains.kotlin.konan.file.* + +typealias Properties = java.util.Properties + +fun File.loadProperties(): Properties { + val properties = java.util.Properties() + this.bufferedReader().use { reader -> + properties.load(reader) + } + return properties +} + +fun loadProperties(path: String): Properties = File(path).loadProperties() + +fun File.saveProperties(properties: Properties) { + this.outputStream().use { + properties.store(it, null) + } +} + +fun Properties.saveToFile(file: File) = file.saveProperties(this) + +fun Properties.propertyString(key: String, suffix: String? = null): String? = getProperty(key.suffix(suffix)) ?: this.getProperty(key) + +/** + * TODO: this method working with suffixes should be replaced with + * functionality borrowed from def file parser and unified for interop tool + * and kotlin compiler. + */ +fun Properties.propertyList(key: String, suffix: String? = null): List { + val value = this.getProperty(key.suffix(suffix)) ?: this.getProperty(key) + if (value?.isBlank() == true) return emptyList() + + return value?.split(Regex("\\s+")) ?: emptyList() +} + +fun Properties.hasProperty(key: String, suffix: String? = null): Boolean = this.getProperty(key.suffix(suffix)) != null + +fun String.suffix(suf: String?): String = if (suf == null) this else "${this}.$suf" + diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/util/ProfilingUtils.kt b/konan/utils/src/org/jetbrains/kotlin/konan/util/ProfilingUtils.kt new file mode 100644 index 00000000000..5796fa95add --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/util/ProfilingUtils.kt @@ -0,0 +1,23 @@ +/* + * 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.kotlin.konan.util + +import kotlin.system.measureTimeMillis + +fun printMillisec(message: String, body: () -> Unit) { + val msec = measureTimeMillis { + body() + } + println("$message: $msec msec") +} + +fun profile(message: String, body: () -> Unit) = profileIf( + System.getProperty("konan.profile")?.equals("true") ?: false, + message, body +) + +fun profileIf(condition: Boolean, message: String, body: () -> Unit) = + if (condition) printMillisec(message, body) else body() diff --git a/konan/utils/src/org/jetbrains/kotlin/konan/util/Substitution.kt b/konan/utils/src/org/jetbrains/kotlin/konan/util/Substitution.kt new file mode 100644 index 00000000000..f8325a0682e --- /dev/null +++ b/konan/utils/src/org/jetbrains/kotlin/konan/util/Substitution.kt @@ -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.kotlin.konan.util + +import org.jetbrains.kotlin.konan.target.KonanTarget +import java.util.* + +fun defaultTargetSubstitutions(target: KonanTarget) = + mapOf( + "target" to target.visibleName, + "arch" to target.architecture.visibleName, + "family" to target.family.visibleName + ) + +// Performs substitution similar to: +// foo = ${foo} ${foo.${arch}} ${foo.${os}} +fun substitute(properties: Properties, substitutions: Map) { + for (key in properties.stringPropertyNames()) { + for (substitution in substitutions.values) { + val suffix = ".$substitution" + if (key.endsWith(suffix)) { + val baseKey = key.removeSuffix(suffix) + val oldValue = properties.getProperty(baseKey, "") + val appendedValue = properties.getProperty(key, "") + val newValue = if (oldValue != "") "$oldValue $appendedValue" else appendedValue + properties.setProperty(baseKey, newValue) + } + } + } +} diff --git a/settings.gradle b/settings.gradle index 425a397eda1..f96b3d54bec 100644 --- a/settings.gradle +++ b/settings.gradle @@ -51,6 +51,7 @@ include ":kotlin-build-common", ":js:js.tests", ":konan:konan-utils", ":konan:konan-metadata", + ":konan:konan-serializer", ":jps-plugin", ":kotlin-jps-plugin", ":core:descriptors", @@ -240,6 +241,7 @@ project(':compiler:ir.backend.common').projectDir = "$rootDir/compiler/ir/backen project(':compiler:backend.js').projectDir = "$rootDir/compiler/ir/backend.js" as File project(':konan:konan-utils').projectDir = "$rootDir/konan/utils" as File project(':konan:konan-metadata').projectDir = "$rootDir/konan/metadata" as File +project(':konan:konan-serializer').projectDir = "$rootDir/konan/serializer" as File project(':kotlin-jps-plugin').projectDir = "$rootDir/prepare/jps-plugin" as File project(':idea:idea-android-output-parser').projectDir = "$rootDir/idea/idea-android/idea-android-output-parser" as File project(':plugins:android-extensions-compiler').projectDir = "$rootDir/plugins/android-extensions/android-extensions-compiler" as File