diff --git a/.idea/dictionaries/dmitriy_dolovov.xml b/.idea/dictionaries/dmitriy_dolovov.xml index 3af0fb03b93..526e23fd489 100644 --- a/.idea/dictionaries/dmitriy_dolovov.xml +++ b/.idea/dictionaries/dmitriy_dolovov.xml @@ -1,6 +1,10 @@ + commonization + commonize + commonized + commonizer konan diff --git a/konan/commonizer/build.gradle.kts b/konan/commonizer/build.gradle.kts new file mode 100644 index 00000000000..b7203414a58 --- /dev/null +++ b/konan/commonizer/build.gradle.kts @@ -0,0 +1,52 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compileOnly(project(":core:util.runtime")) + compileOnly(project(":core:descriptors")) + + compile(kotlinStdlib()) + + testCompile(commonDep("junit:junit")) + testCompile(projectTests(":compiler:tests-common")) + + testCompile(intellijCoreDep()) { includeJars("intellij-core") } + testCompile(intellijDep()) { + includeJars( + "openapi", + "jps-model", + "extensions", + "util", + "platform-api", + "platform-impl", + "idea", + "idea_rt", + "guava", + "trove4j", + "picocontainer", + "asm-all", + "log4j", + "jdom", + "streamex", + "bootstrap", + rootProject = rootProject + ) + isTransitive = false + } + + Platform[192].orHigher { + testCompile(intellijDep()) { includeJars("platform-util-ui", "platform-concurrency", "platform-objectSerializer") } + } +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +projectTest(parallel = true) { + dependsOn(":dist") + workingDir = rootDir +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/TargetId.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/TargetId.kt new file mode 100644 index 00000000000..82faf106d0f --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/TargetId.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +// N.B. TargetPlatform/SimplePlatform are non exhaustive enough to address both target platforms such as +// JVM, JS and concrete Kotlin/Native targets, e.g. macos_x64, ios_x64, linux_x64. +sealed class TargetId + +data class ConcreteTargetId(val name: String) : TargetId() + +data class CommonTargetId(val targets: Set) : TargetId() { + init { + require(targets.isNotEmpty()) + } +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedMemberScope.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedMemberScope.kt new file mode 100644 index 00000000000..24c50a464fa --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedMemberScope.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.builder + +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor +import org.jetbrains.kotlin.incremental.components.LookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl +import org.jetbrains.kotlin.utils.Printer + +internal class CommonizedMemberScope : MemberScopeImpl() { + private val members = ArrayList() + + operator fun plusAssign(member: DeclarationDescriptor) { + this.members += member + } + + override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = + members.filteredBy(name).firstOrNull() + + override fun getContributedVariables(name: Name, location: LookupLocation): Collection = + members.filteredBy(name).toList() + + override fun getContributedFunctions(name: Name, location: LookupLocation): Collection = + members.filteredBy(name).toList() + + override fun getContributedDescriptors( + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean + ): Collection = members.filter { kindFilter.accepts(it) && nameFilter(it.name) } + + override fun printScopeStructure(p: Printer) { + p.println(this::class.java.simpleName, " {") + p.pushIndent() + + p.println("declarations = $members") + + p.popIndent() + p.println("}") + } + + companion object { + fun createArray(size: Int) = Array(size) { CommonizedMemberScope() } + + operator fun Array.plusAssign(members: List) { + members.forEachIndexed { index, member -> + this[index] += member ?: return@forEachIndexed + } + } + } +} + +private inline fun List<*>.filteredBy(name: Name): Sequence = + this.asSequence().filterIsInstance().filter { it.name == name } diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedPackageFragmentProvider.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedPackageFragmentProvider.kt new file mode 100644 index 00000000000..fb5227e153f --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/CommonizedPackageFragmentProvider.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.builder + +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +internal class CommonizedPackageFragmentProvider : PackageFragmentProvider { + private val packageFragments = ArrayList() + + operator fun plusAssign(packageFragment: PackageFragmentDescriptor) { + this.packageFragments += packageFragment + } + + override fun getPackageFragments(fqName: FqName): List = + packageFragments.filter { it.fqName == fqName } + + override fun getSubPackagesOf(fqName: FqName, nameFilter: (Name) -> Boolean): Collection = + packageFragments.asSequence() + .map { it.fqName } + .filter { !it.isRoot && it.parent() == fqName } + .toList() + + companion object { + fun createArray(size: Int) = Array(size) { CommonizedPackageFragmentProvider() } + + operator fun Array.plusAssign(packageFragments: List) { + packageFragments.forEachIndexed { index, packageFragment -> + this[index] += packageFragment ?: return@forEachIndexed + } + } + } +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/DeclarationsBuilderVisitor.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/DeclarationsBuilderVisitor.kt new file mode 100644 index 00000000000..5d76000576c --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/DeclarationsBuilderVisitor.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.builder + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroup +import org.jetbrains.kotlin.descriptors.commonizer.TargetId +import org.jetbrains.kotlin.descriptors.commonizer.builder.CommonizedMemberScope.Companion.plusAssign +import org.jetbrains.kotlin.descriptors.commonizer.builder.CommonizedPackageFragmentProvider.Companion.plusAssign +import org.jetbrains.kotlin.descriptors.commonizer.ir.* +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.utils.addIfNotNull + +internal class DeclarationsBuilderVisitor( + private val storageManager: StorageManager, + private val builtIns: KotlinBuiltIns, + private val collector: (TargetId, Collection) -> Unit +) : NodeVisitor, List> { + override fun visitRootNode(node: RootNode, data: List): List { + val allTargets = (node.target + node.common).map { it.targetId } + + val modulesByTargets = HashMap>() + + // collect module descriptors: + for (moduleNode in node.modules) { + val modules = moduleNode.accept(this, noContainingDeclarations()).asListContaining() + modules.forEachIndexed { index, module -> + val target = allTargets[index] + modulesByTargets.computeIfAbsent(target) { mutableListOf() }.addIfNotNull(module) + } + } + + // set cross-module dependencies: + modulesByTargets.values.forEach { modulesSameTarget -> + for (module in modulesSameTarget) + module.setDependencies(modulesSameTarget) + } + + // return result (preserve platforms order): + for (target in allTargets) { + collector(target, modulesByTargets[target]!!) + } + + return noReturningDeclarations() + } + + override fun visitModuleNode(node: ModuleNode, data: List): List { + // build module descriptors: + val moduleDescriptorsGroup = CommonizedGroup(node.dimension) + node.buildDescriptors(moduleDescriptorsGroup, storageManager, builtIns) + val moduleDescriptors = moduleDescriptorsGroup.toList() + + // build package fragments: + val packageFragmentProviders = CommonizedPackageFragmentProvider.createArray(node.dimension) + for (packageNode in node.packages) { + val packageFragments = packageNode.accept(this, moduleDescriptors).asListContaining() + packageFragmentProviders += packageFragments + } + + // initialize module descriptors: + moduleDescriptors.asListContaining().forEachIndexed { index, moduleDescriptor -> + moduleDescriptor?.initialize(packageFragmentProviders[index]) + } + + return moduleDescriptors + } + + override fun visitPackageNode(node: PackageNode, data: List): List { + val containingDeclarations = data.asListContaining() + + // build package fragments: + val packageFragmentsGroup = CommonizedGroup(node.dimension) + node.buildDescriptors(packageFragmentsGroup, containingDeclarations) + val packageFragments = packageFragmentsGroup.toList() + + // build package members: + val packageMemberScopes = CommonizedMemberScope.createArray(node.dimension) + for (propertyNode in node.properties) { + packageMemberScopes += propertyNode.accept(this, packageFragments) + } + + // initialize package fragments: + packageFragments.forEachIndexed { index, packageFragment -> + packageFragment?.initialize(packageMemberScopes[index]) + } + + return packageFragments + } + + override fun visitPropertyNode(node: PropertyNode, data: List): List { + val propertyDescriptorsGroup = CommonizedGroup(node.dimension) + node.buildDescriptors(propertyDescriptorsGroup, data, storageManager) + + return propertyDescriptorsGroup.toList() + } + + companion object { + inline fun noContainingDeclarations() = emptyList() + inline fun noReturningDeclarations() = emptyList() + } +} + +@Suppress("UNCHECKED_CAST") +private inline fun List.asListContaining(): List = + this as List diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/moduleDescriptors.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/moduleDescriptors.kt new file mode 100644 index 00000000000..b531570e85e --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/moduleDescriptors.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.builder + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroup +import org.jetbrains.kotlin.descriptors.commonizer.ir.Module +import org.jetbrains.kotlin.descriptors.commonizer.ir.ModuleNode +import org.jetbrains.kotlin.descriptors.commonizer.ir.indexOfCommon +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.storage.StorageManager + +internal fun ModuleNode.buildDescriptors( + output: CommonizedGroup, + storageManager: StorageManager, + builtIns: KotlinBuiltIns +) { + target.forEachIndexed { index, module -> + module?.buildDescriptor(output, index, storageManager, builtIns) + } + + common?.buildDescriptor(output, indexOfCommon, storageManager, builtIns) +} + +private fun Module.buildDescriptor( + output: CommonizedGroup, + index: Int, + storageManager: StorageManager, + builtIns: KotlinBuiltIns +) { + val moduleDescriptor = ModuleDescriptorImpl( + moduleName = name, + storageManager = storageManager, + builtIns = builtIns, + capabilities = emptyMap() // TODO: specify capabilities + ) + + output[index] = moduleDescriptor +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/packageFragments.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/packageFragments.kt new file mode 100644 index 00000000000..20f5e5e4407 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/packageFragments.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.builder + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroup +import org.jetbrains.kotlin.descriptors.commonizer.ir.Package +import org.jetbrains.kotlin.descriptors.commonizer.ir.PackageNode +import org.jetbrains.kotlin.descriptors.commonizer.ir.indexOfCommon +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.scopes.MemberScope + +internal fun PackageNode.buildDescriptors( + output: CommonizedGroup, + modules: List +) { + target.forEachIndexed { index, pkg -> + pkg?.buildDescriptor(output, index, modules) + } + + common?.buildDescriptor(output, indexOfCommon, modules) +} + +private fun Package.buildDescriptor( + output: CommonizedGroup, + index: Int, + modules: List +) { + val module = modules[index] ?: error("No containing declaration for package $this") + val packageFragment = CommonizedPackageFragmentDescriptor(module, fqName) + output[index] = packageFragment +} + +internal class CommonizedPackageFragmentDescriptor( + module: ModuleDescriptor, + fqName: FqName +) : PackageFragmentDescriptorImpl(module, fqName) { + private lateinit var memberScope: MemberScope + + fun initialize(memberScope: MemberScope) { + this.memberScope = memberScope + } + + override fun getMemberScope() = memberScope +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/propertyDescriptors.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/propertyDescriptors.kt new file mode 100644 index 00000000000..dab211df44b --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/builder/propertyDescriptors.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.builder + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroup +import org.jetbrains.kotlin.descriptors.commonizer.ir.Property +import org.jetbrains.kotlin.descriptors.commonizer.ir.PropertyNode +import org.jetbrains.kotlin.descriptors.commonizer.ir.indexOfCommon +import org.jetbrains.kotlin.descriptors.impl.FieldDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.resolve.DescriptorFactory +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.storage.StorageManager + +internal fun PropertyNode.buildDescriptors( + output: CommonizedGroup, + containingDeclarations: List, + storageManager: StorageManager +) { + val isCommonized = common != null + + target.forEachIndexed { index, property -> + property?.buildDescriptor(output, index, containingDeclarations, storageManager, isActual = isCommonized) + } + + common?.buildDescriptor(output, indexOfCommon, containingDeclarations, storageManager, isExpect = isCommonized) +} + +private fun Property.buildDescriptor( + output: CommonizedGroup, + index: Int, + containingDeclarations: List, + storageManager: StorageManager, + isExpect: Boolean = false, + isActual: Boolean = false +) { + val containingDeclaration = containingDeclarations[index] ?: error("No containing declaration for property $this") + + val propertyDescriptor = PropertyDescriptorImpl.create( + containingDeclaration, + annotations, + modality, + visibility, + isVar, + name, + kind, + SourceElement.NO_SOURCE, + lateInit, + isConst, + isExpect, + isActual, + isExternal, + isDelegate + ) + + val extensionReceiverDescriptor = DescriptorFactory.createExtensionReceiverParameterForCallable( + propertyDescriptor, + extensionReceiverType, + Annotations.EMPTY + ) + + val dispatchReceiverDescriptor = DescriptorUtils.getDispatchReceiverParameterIfNeeded(containingDeclaration) + + propertyDescriptor.setType( + type, + emptyList(), // TODO: support type parameters + dispatchReceiverDescriptor, + extensionReceiverDescriptor + ) + + val getterDescriptor = getter?.let { getter -> + DescriptorFactory.createGetter( + propertyDescriptor, + getter.annotations, + getter.isDefault, + getter.isExternal, + getter.isInline + ).apply { + initialize(null) // use return type from the property descriptor + } + } + + val setterDescriptor = setter?.let { setter -> + DescriptorFactory.createSetter( + propertyDescriptor, + setter.annotations, + setter.parameterAnnotations, + setter.isDefault, + setter.isExternal, + setter.isInline, + setter.visibility, + SourceElement.NO_SOURCE + ) + } + + val backingField = backingFieldAnnotations?.let { FieldDescriptorImpl(it, propertyDescriptor) } + val delegateField = delegateFieldAnnotations?.let { FieldDescriptorImpl(it, propertyDescriptor) } + + propertyDescriptor.initialize( + getterDescriptor, + setterDescriptor, + backingField, + delegateField + ) + + compileTimeInitializer?.let { constantValue -> + propertyDescriptor.setCompileTimeInitializer(storageManager.createNullableLazyValue { constantValue }) + } + + output[index] = propertyDescriptor +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/commonizedGroup.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/commonizedGroup.kt new file mode 100644 index 00000000000..0ca599d3a74 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/commonizedGroup.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +// Fixed-size list that represents a commonized group of same-rank elements +internal class CommonizedGroup( + val size: Int, + initialize: (Int) -> T? +) { + constructor(elements: List) : this(elements.size, elements::get) + + constructor(size: Int) : this(size, { null }) + + // array constructor requires concrete type which is known only at the call site, + // so let's use `Any?` instead if `T` here + private val elements = Array(size, initialize) + + operator fun get(index: Int): T? { + @Suppress("UNCHECKED_CAST") + return elements[index] as T? + } + + operator fun set(index: Int, value: T) { + val oldValue = this[index] + check(oldValue == null) { "$oldValue can not be overwritten with $value at index $index" } + + elements[index] = value + } + + fun toList(): List = object : AbstractList() { + override val size + get() = this@CommonizedGroup.size + + override fun get(index: Int): T? = this@CommonizedGroup[index] + } +} + +internal class CommonizedGroupMap(val size: Int) : Iterable>> { + private val wrapped: MutableMap> = HashMap() + + operator fun get(key: K): CommonizedGroup = wrapped.getOrPut(key) { CommonizedGroup(size) } + + override fun iterator(): Iterator>> = wrapped.iterator() +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt new file mode 100644 index 00000000000..ce833202e99 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/Commonizer.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +interface Commonizer { + val result: R + fun commonizeWith(next: T): Boolean +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ExtensionReceiverCommonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ExtensionReceiverCommonizer.kt new file mode 100644 index 00000000000..309a5cf7c97 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ExtensionReceiverCommonizer.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.types.UnwrappedType + +interface ExtensionReceiverCommonizer : Commonizer { + companion object { + fun default(): ExtensionReceiverCommonizer = DefaultExtensionReceiverCommonizer() + } +} + +private class DefaultExtensionReceiverCommonizer : ExtensionReceiverCommonizer { + private enum class State { + EMPTY, + ERROR, + WITH_RECEIVER, + WITHOUT_RECEIVER + } + + private var state = State.EMPTY + private var receiverType: TypeCommonizer? = null + + override val result: UnwrappedType? + get() = when (state) { + State.EMPTY, State.ERROR -> error("Receiver parameter type can't be commonized") + State.WITH_RECEIVER -> receiverType!!.result + State.WITHOUT_RECEIVER -> null // null receiverType means there is no extension receiver + } + + override fun commonizeWith(next: ReceiverParameterDescriptor?): Boolean { + state = when (state) { + State.ERROR -> State.ERROR + State.EMPTY -> next?.let { + receiverType = TypeCommonizer.default() + doCommonizeWith(next) + } ?: State.WITHOUT_RECEIVER + State.WITH_RECEIVER -> next?.let(::doCommonizeWith) ?: State.ERROR + State.WITHOUT_RECEIVER -> next?.let { State.ERROR } ?: State.WITHOUT_RECEIVER + } + + return state != State.ERROR + } + + private fun doCommonizeWith(receiverParameter: ReceiverParameterDescriptor) = + if (receiverType!!.commonizeWith(receiverParameter.type)) State.WITH_RECEIVER else State.ERROR +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ModalityCommonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ModalityCommonizer.kt new file mode 100644 index 00000000000..2d638fefbae --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/ModalityCommonizer.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.Modality + +interface ModalityCommonizer : Commonizer { + companion object { + fun default(): ModalityCommonizer = DefaultModalityCommonizer() + } +} + +private class DefaultModalityCommonizer : ModalityCommonizer { + private enum class State { + EMPTY { + override fun getNext(modality: Modality) = when (modality) { + Modality.ABSTRACT -> CAN_HAVE_ONLY_ABSTRACT + Modality.SEALED -> CAN_HAVE_ONLY_SEALED + Modality.FINAL -> HAS_FINAL + Modality.OPEN -> HAS_OPEN + } + }, + ERROR { + override fun getNext(modality: Modality) = ERROR + }, + CAN_HAVE_ONLY_SEALED { + override fun getNext(modality: Modality) = if (modality == Modality.SEALED) this else ERROR + }, + CAN_HAVE_ONLY_ABSTRACT { + override fun getNext(modality: Modality) = if (modality == Modality.ABSTRACT) this else ERROR + }, + HAS_FINAL { + override fun getNext(modality: Modality) = when (modality) { + Modality.FINAL -> this + Modality.OPEN -> HAS_FINAL_AND_OPEN + else -> ERROR + } + }, + HAS_OPEN { + override fun getNext(modality: Modality) = when (modality) { + Modality.FINAL -> HAS_FINAL_AND_OPEN + Modality.OPEN -> this + else -> ERROR + } + }, + HAS_FINAL_AND_OPEN { + override fun getNext(modality: Modality) = when (modality) { + Modality.FINAL, Modality.OPEN -> this + else -> ERROR + } + }; + + abstract fun getNext(modality: Modality): State + } + + private var state = State.EMPTY + + override val result: Modality + get() = when (state) { + State.CAN_HAVE_ONLY_SEALED -> Modality.SEALED + State.CAN_HAVE_ONLY_ABSTRACT -> Modality.ABSTRACT + State.HAS_FINAL, State.HAS_FINAL_AND_OPEN -> Modality.FINAL + State.HAS_OPEN -> Modality.OPEN + else -> error("Modality can't be commonized") + } + + override fun commonizeWith(next: Modality): Boolean { + state = state.getNext(next) + return state != State.ERROR + } +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/PropertyCommonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/PropertyCommonizer.kt new file mode 100644 index 00000000000..62d88cb363c --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/PropertyCommonizer.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.ir.CommonProperty +import org.jetbrains.kotlin.descriptors.commonizer.ir.Property +import org.jetbrains.kotlin.name.Name + +class PropertyCommonizer : Commonizer { + private enum class State { + EMPTY, + ERROR, + IN_PROGRESS + } + + private var name: Name? = null + // TODO: visibility - what if virtual declaration? + private val visibility = VisibilityCommonizer.lowering() + private val modality = ModalityCommonizer.default() + private val returnType = TypeCommonizer.default() + private val setter = PropertySetterCommonizer.default() + private val extensionReceiver = ExtensionReceiverCommonizer.default() + + private var state = State.EMPTY + + override val result: Property + get() = when (state) { + State.EMPTY, State.ERROR -> error("Can't commonize property") + State.IN_PROGRESS -> CommonProperty( + name = name!!, + visibility = visibility.result, + modality = modality.result, + type = returnType.result, + setter = setter.result, + extensionReceiverType = extensionReceiver.result + ) + } + + override fun commonizeWith(next: PropertyDescriptor): Boolean { + if (state == State.ERROR) + return false + + if (name == null) + name = next.name + + val result = canBeCommonized(next) + && visibility.commonizeWith(next.visibility) + && modality.commonizeWith(next.modality) + && returnType.commonizeWith(next.type) + && setter.commonizeWith(next.setter) + && extensionReceiver.commonizeWith(next.extensionReceiverParameter) + + // TODO: type parameters (for properties???) + + state = if (!result) State.ERROR else State.IN_PROGRESS + + return result + } + + private fun canBeCommonized(property: PropertyDescriptor) = when { + property.isConst -> false // expect property can't be const because expect can't have initializer + property.isLateInit -> false // expect property can't be lateinit + else -> true + } +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/PropertySetterCommonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/PropertySetterCommonizer.kt new file mode 100644 index 00000000000..73b958691a6 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/PropertySetterCommonizer.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.ir.Setter + +interface PropertySetterCommonizer : Commonizer { + companion object { + fun default(): PropertySetterCommonizer = DefaultPropertySetterCommonizer() + } +} + +private class DefaultPropertySetterCommonizer : PropertySetterCommonizer { + private enum class State { + EMPTY, + ERROR, + WITH_SETTER, + WITHOUT_SETTER + } + + private var state = State.EMPTY + private var setterVisibility: VisibilityCommonizer? = null + + override val result: Setter? + get() = when (state) { + State.EMPTY, State.ERROR -> error("Property setter can't be commonized") + State.WITH_SETTER -> Setter.createDefaultNoAnnotations(setterVisibility!!.result) + State.WITHOUT_SETTER -> null // null visibility means there is no setter + } + + override fun commonizeWith(next: PropertySetterDescriptor?): Boolean { + state = when (state) { + State.ERROR -> State.ERROR + State.EMPTY -> next?.let { + setterVisibility = VisibilityCommonizer.lowering() + doCommonizeWith(next) + } ?: State.WITHOUT_SETTER + State.WITH_SETTER -> next?.let(::doCommonizeWith) ?: State.ERROR + State.WITHOUT_SETTER -> next?.let { State.ERROR } ?: State.WITHOUT_SETTER + } + + return state != State.ERROR + } + + private fun doCommonizeWith(setter: PropertySetterDescriptor) = + if (setterVisibility!!.commonizeWith(setter.visibility)) State.WITH_SETTER else State.ERROR +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizer.kt new file mode 100644 index 00000000000..f0f702ec88a --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/TypeCommonizer.kt @@ -0,0 +1,181 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.isClassType + +interface TypeCommonizer : Commonizer { + companion object { + fun default(): TypeCommonizer = DefaultTypeCommonizer() + } +} + +private class DefaultTypeCommonizer : TypeCommonizer { + private enum class State { + EMPTY, + ERROR, + IN_PROGRESS + } + + private var state = State.EMPTY + private var temp: UnwrappedType? = null + + override val result: UnwrappedType + get() = when (state) { + State.EMPTY, State.ERROR -> error("Can't commonize type") + State.IN_PROGRESS -> temp!! + } + + override fun commonizeWith(next: KotlinType): Boolean { + state = when (state) { + State.ERROR -> State.ERROR + State.EMPTY -> { + temp = next.unwrap() + State.IN_PROGRESS + } + // TODO: maybe cache type comparison results? + State.IN_PROGRESS -> { + if (!areTypesEqual(temp!!, next.unwrap())) State.ERROR else State.IN_PROGRESS + } + } + + return state != State.ERROR + } +} + +/** + * See also [AbstractStrictEqualityTypeChecker]. + */ +internal fun areTypesEqual(a: UnwrappedType, b: UnwrappedType): Boolean = when { + a === b -> true + a is SimpleType -> (b is SimpleType) && areSimpleTypesEqual(a, b) + a is FlexibleType -> (b is FlexibleType) + && areSimpleTypesEqual(a.lowerBound, b.lowerBound) && areSimpleTypesEqual(a.upperBound, b.upperBound) + else -> false +} + +private fun areSimpleTypesEqual(a: SimpleType, b: SimpleType): Boolean = areAbbreviatedTypesEqual( + a = a.getAbbreviation() ?: a, + aExpanded = a, + b = b.getAbbreviation() ?: b, + bExpandedType = b +) + +private fun areAbbreviatedTypesEqual(a: SimpleType, aExpanded: SimpleType, b: SimpleType, bExpandedType: SimpleType): Boolean { + if (a.arguments.size != b.arguments.size + || a.isMarkedNullable != b.isMarkedNullable + || a.isDefinitelyNotNullType != b.isDefinitelyNotNullType + ) { + return false + } + + val aDescriptor = requireNotNull(a.constructor.declarationDescriptor, a::nonNullDescriptorExpectedErrorMessage) + val bDescriptor = requireNotNull(b.constructor.declarationDescriptor, b::nonNullDescriptorExpectedErrorMessage) + + val aFqName = aDescriptor.fqNameSafe + val bFqName = bDescriptor.fqNameSafe + + if (aFqName.isUnderStandardKotlinPackages || bFqName.isUnderStandardKotlinPackages) { + // make sure that FQ names of abbreviated types (e.g. representing type aliases) are equal + return aFqName == bFqName + // if classes are from the standard Kotlin packages, compare them only by type constructors + // effectively, this includes 1) comparison of FQ names and 2) number of type constructor parameters + // see org.jetbrains.kotlin.types.AbstractClassTypeConstructor.equals() for details + && aExpanded.constructor == bExpandedType.constructor + } + + val descriptorsCanBeCommonized = when (aDescriptor) { + is TypeAliasDescriptor -> (bDescriptor is TypeAliasDescriptor) && canBeCommonized(aDescriptor, bDescriptor) + is ClassDescriptor -> (bDescriptor is ClassDescriptor) && canBeCommonized(aDescriptor, bDescriptor) + else -> false + } + + if (!descriptorsCanBeCommonized) + return false + + if (a.arguments === b.arguments) + return true + + for (i in 0 until a.arguments.size) { + val aArg = a.arguments[i] + val bArg = b.arguments[i] + + if (aArg.isStarProjection != bArg.isStarProjection) + return false + + if (!aArg.isStarProjection) { + if (aArg.projectionKind != bArg.projectionKind) + return false + + if (!areTypesEqual(aArg.type.unwrap(), bArg.type.unwrap())) + return false + } + } + + return true +} + +@Suppress("NOTHING_TO_INLINE") +private inline fun SimpleType.nonNullDescriptorExpectedErrorMessage() = + "${TypeCommonizer::class} couldn't obtain non-null descriptor from: $this, ${this::class}" + +private val standardKotlinPackages = setOf( + KotlinBuiltIns.BUILT_INS_PACKAGE_NAME, + Name.identifier("kotlinx") +) + +private val FqName.isUnderStandardKotlinPackages: Boolean + get() = pathSegments().firstOrNull() in standardKotlinPackages + +// TODO: extract this method to class commonizer +private fun canBeCommonized(a: ClassDescriptor, b: ClassDescriptor) = when { + a.kind != b.kind -> false + !areFqNamesEqual(a, b) -> false + // TODO: compare class descriptors (visibility, modifiers, etc) + else -> true +} + +// TODO: extract this method to type alias commonizer +private fun canBeCommonized(a: TypeAliasDescriptor, b: TypeAliasDescriptor): Boolean { + if (!areFqNamesEqual(a, b)) + return false + + val aUnderlyingType = a.underlyingType + val bUnderlyingType = b.underlyingType + + if (aUnderlyingType.arguments.isNotEmpty() || bUnderlyingType.arguments.isNotEmpty()) + return false // type aliases with functional types at the right-hand side can't be commonized + + if (!aUnderlyingType.isClassType || !bUnderlyingType.isClassType) + return false // right-hand side could have only classes + + return areTypesEqual(aUnderlyingType, bUnderlyingType) +} + +private fun areFqNamesEqual(d1: T, d2: T): Boolean { + val p1 = d1.parentsWithSelf.iterator() + val p2 = d2.parentsWithSelf.iterator() + + while (p1.hasNext() && p2.hasNext()) { + val n1 = p1.next() + val n2 = p2.next() + + when (n1) { + is ModuleDescriptor -> return n2 is ModuleDescriptor + is PackageFragmentDescriptor -> return (n2 is PackageFragmentDescriptor) && n1.fqName == n2.fqName + else -> if (n1.name != n2.name) return false + } + } + + return false +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/VisibilityCommonizer.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/VisibilityCommonizer.kt new file mode 100644 index 00000000000..db518ccdddf --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/core/VisibilityCommonizer.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility + +abstract class VisibilityCommonizer : Commonizer { + + companion object { + fun lowering(): VisibilityCommonizer = LoweringVisibilityCommonizer() + fun equalizing(): VisibilityCommonizer = EqualizingVisibilityCommonizer() + } + + private var temp: Visibility? = null + + override val result: Visibility + get() { + return temp?.takeIf { it != Visibilities.UNKNOWN } ?: error("Visibility can't be commonized") + } + + override fun commonizeWith(next: Visibility): Boolean { + if (temp == Visibilities.UNKNOWN) + return false + + if (Visibilities.isPrivate(next)) { + temp = Visibilities.UNKNOWN + return false + } + + temp = temp?.let { temp -> getNext(temp, next) } ?: next + + return temp != Visibilities.UNKNOWN + } + + protected abstract fun getNext(current: Visibility, next: Visibility): Visibility +} + +private class LoweringVisibilityCommonizer : VisibilityCommonizer() { + override fun getNext(current: Visibility, next: Visibility): Visibility { + val comparisonResult: Int = Visibilities.compare(current, next) + ?: return Visibilities.UNKNOWN // two visibilities that can't be compared against each one, ex: protected vs internal + + return if (comparisonResult <= 0) current else next + } +} + +private class EqualizingVisibilityCommonizer : VisibilityCommonizer() { + override fun getNext(current: Visibility, next: Visibility) = + if (Visibilities.compare(current, next) == 0) current else Visibilities.UNKNOWN +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt new file mode 100644 index 00000000000..0b300da5a59 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/facade.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +import org.jetbrains.kotlin.builtins.DefaultBuiltIns +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.builder.DeclarationsBuilderVisitor +import org.jetbrains.kotlin.descriptors.commonizer.mergedtree.mergeRoots +import org.jetbrains.kotlin.storage.LockBasedStorageManager + +class CommonizationSession { + // TODO: add progress tracker + // TODO: add logger +} + +class CommonizationParameters { + private val modulesByTargets = LinkedHashMap>() + + fun addTarget(targetName: String, modules: Collection): CommonizationParameters { + val targetId = ConcreteTargetId(targetName) + require(targetId !in modulesByTargets) { + "Target $targetId is already added" + } + + val modulesWithUniqueNames = modules.groupingBy { it.name }.eachCount() + require(modulesWithUniqueNames.size == modules.size) { + "Modules with duplicated names found: ${modulesWithUniqueNames.filter { it.value > 1 }}" + } + + modulesByTargets[targetId] = modules + + return this + } + + // get them as ordered immutable collection (List) for further processing + fun getModulesByTargets(): List>> = + modulesByTargets.map { it.key to it.value } + + fun hasIntersection(): Boolean { + if (modulesByTargets.size < 2) + return false + + return modulesByTargets.flatMap { it.value } + .groupingBy { it.name } + .eachCount() + .any { it.value == modulesByTargets.size } + } +} + +sealed class CommonizationResult + +object NothingToCommonize : CommonizationResult() + +class CommonizationPerformed( + val commonModules: Collection, + val modulesByTargets: Map> +) : CommonizationResult() + +fun runCommonization(parameters: CommonizationParameters): CommonizationResult { + if (!parameters.hasIntersection()) + return NothingToCommonize + + val mergedTree = mergeRoots(parameters.getModulesByTargets()) + + val storageManager = LockBasedStorageManager("Declaration descriptors commonization") + + var commonModules: Collection? = null + val otherModulesByTargets = LinkedHashMap>() + + val visitor = DeclarationsBuilderVisitor(storageManager, DefaultBuiltIns.Instance) { targetId, commonizedModules -> + when (targetId) { + is CommonTargetId -> { + check(commonModules == null) + commonModules = commonizedModules + } + is ConcreteTargetId -> { + val targetName = targetId.name + check(targetName !in otherModulesByTargets) + otherModulesByTargets[targetName] = commonizedModules + } + } + } + mergedTree.accept(visitor, DeclarationsBuilderVisitor.noContainingDeclarations()) + + return CommonizationPerformed(commonModules!!, otherModulesByTargets) +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Module.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Module.kt new file mode 100644 index 00000000000..d9b1d8174f8 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Module.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +import org.jetbrains.kotlin.name.Name + +data class Module( + val name: Name +) : Declaration diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/NodeVisitor.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/NodeVisitor.kt new file mode 100644 index 00000000000..5ecf2f2f0d2 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/NodeVisitor.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +interface NodeVisitor { + fun visitRootNode(node: RootNode, data: T): R + fun visitModuleNode(node: ModuleNode, data: T): R + fun visitPackageNode(node: PackageNode, data: T): R + fun visitPropertyNode(node: PropertyNode, data: T): R +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Package.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Package.kt new file mode 100644 index 00000000000..5c5b0abfa6a --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Package.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +import org.jetbrains.kotlin.name.FqName + +data class Package( + val fqName: FqName +) : Declaration diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Property.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Property.kt new file mode 100644 index 00000000000..719a4aba3f0 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Property.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.descriptors.commonizer.ir.Getter.Companion.toGetter +import org.jetbrains.kotlin.descriptors.commonizer.ir.Setter.Companion.toSetter +import org.jetbrains.kotlin.resolve.constants.ConstantValue + +interface Property : Declaration { + val annotations: Annotations + val name: Name + val visibility: Visibility + val modality: Modality + val isVar: Boolean + val kind: CallableMemberDescriptor.Kind + val type: UnwrappedType + val lateInit: Boolean + val isConst: Boolean + val isExternal: Boolean + val isDelegate: Boolean + val getter: Getter? + val setter: Setter? + val extensionReceiverType: UnwrappedType? + val backingFieldAnnotations: Annotations? // null assumes no backing field + val delegateFieldAnnotations: Annotations? // null assumes no backing field + val compileTimeInitializer: ConstantValue<*>? +} + +data class CommonProperty( + override val name: Name, + override val visibility: Visibility, + override val modality: Modality, + override val type: UnwrappedType, + override val setter: Setter?, + override val extensionReceiverType: UnwrappedType? +) : Property { + override val annotations get() = Annotations.EMPTY + override val isVar: Boolean get() = setter != null + override val kind get() = CallableMemberDescriptor.Kind.DECLARATION + override val lateInit: Boolean get() = false + override val isConst: Boolean get() = false + override val isExternal: Boolean get() = false + override val isDelegate: Boolean get() = false + override val getter: Getter get() = Getter.DEFAULT_NO_ANNOTATIONS + override val backingFieldAnnotations: Annotations? get() = null + override val delegateFieldAnnotations: Annotations? get() = null + override val compileTimeInitializer: ConstantValue<*>? get() = null +} + +data class TargetProperty(private val descriptor: PropertyDescriptor) : Property { + override val annotations: Annotations get() = descriptor.annotations + override val name: Name get() = descriptor.name + override val visibility: Visibility get() = descriptor.visibility + override val modality: Modality get() = descriptor.modality + override val isVar: Boolean get() = descriptor.isVar + override val kind: CallableMemberDescriptor.Kind get() = descriptor.kind + override val type: UnwrappedType get() = descriptor.type.unwrap() + override val lateInit: Boolean get() = descriptor.isLateInit + override val isConst: Boolean get() = descriptor.isConst + override val isExternal: Boolean get() = descriptor.isExternal + @Suppress("DEPRECATION") + override val isDelegate: Boolean get() = descriptor.isDelegated + override val getter: Getter? get() = descriptor.getter?.toGetter() + override val setter: Setter? get() = descriptor.setter?.toSetter() + override val extensionReceiverType: UnwrappedType? get() = descriptor.extensionReceiverParameter?.type?.unwrap() + override val backingFieldAnnotations: Annotations? get() = descriptor.backingField?.annotations + override val delegateFieldAnnotations: Annotations? get() = descriptor.delegateField?.annotations + override val compileTimeInitializer: ConstantValue<*>? get() = descriptor.compileTimeInitializer +} + +interface PropertyAccessor { + val annotations: Annotations + val isDefault: Boolean + val isExternal: Boolean + val isInline: Boolean +} + +data class Getter( + override val annotations: Annotations, + override val isDefault: Boolean, + override val isExternal: Boolean, + override val isInline: Boolean +) : PropertyAccessor { + companion object { + val DEFAULT_NO_ANNOTATIONS = Getter(Annotations.EMPTY, isDefault = true, isExternal = false, isInline = false) + + fun PropertyGetterDescriptor.toGetter() = + if (isDefault && annotations.isEmpty()) + DEFAULT_NO_ANNOTATIONS + else + Getter(annotations, isDefault, isExternal, isInline) + } +} + +data class Setter( + override val annotations: Annotations, + val parameterAnnotations: Annotations, + val visibility: Visibility, + override val isDefault: Boolean, + override val isExternal: Boolean, + override val isInline: Boolean +) : PropertyAccessor { + companion object { + fun createDefaultNoAnnotations(visibility: Visibility) = Setter( + Annotations.EMPTY, + Annotations.EMPTY, + visibility, + isDefault = true, + isExternal = false, + isInline = false + ) + + fun PropertySetterDescriptor.toSetter() = Setter( + annotations, + valueParameters.single().annotations, + visibility, + isDefault, + isExternal, + isInline + ) + } +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Root.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Root.kt new file mode 100644 index 00000000000..5639fbf22aa --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/Root.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +import org.jetbrains.kotlin.descriptors.commonizer.TargetId + +data class Root(val targetId: TargetId) : Declaration diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/nodeBuilders.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/nodeBuilders.kt new file mode 100644 index 00000000000..5532fba130e --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/nodeBuilders.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.* +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroup +import org.jetbrains.kotlin.descriptors.commonizer.core.Commonizer +import org.jetbrains.kotlin.descriptors.commonizer.core.PropertyCommonizer +import org.jetbrains.kotlin.descriptors.commonizer.firstNonNull +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.scopes.MemberScope + +internal fun buildRootNode(targets: List): RootNode = RootNode( + targets.map { Root(it) }, + Root(CommonTargetId(targets.toSet())) +) + +internal fun buildModuleNode(modules: List): ModuleNode = buildNode( + modules, + { Module(it.name) }, + { Module(it.firstNonNull().name) }, + ::ModuleNode +) + +internal fun buildPackageNode(packageFqName: FqName, packageMemberScopes: List): PackageNode = buildNode( + packageMemberScopes, + { Package(packageFqName) }, + { Package(packageFqName) }, + ::PackageNode +) + +internal fun buildPropertyNode(properties: List): PropertyNode = buildNode( + properties, + { TargetProperty(it) }, + { commonize(it, PropertyCommonizer()) }, + ::PropertyNode +) + +private fun > buildNode( + descriptors: List, + targetDeclarationProducer: (T) -> D, + commonDeclarationProducer: (List) -> D?, + nodeProducer: (List, D?) -> N +): N { + val target = CommonizedGroup(descriptors.size) + var canHaveCommon = descriptors.size > 1 + + descriptors.forEachIndexed { index, descriptor -> + if (descriptor != null) + target[index] = targetDeclarationProducer(descriptor) + else + canHaveCommon = false + } + + val common = if (canHaveCommon) commonDeclarationProducer(descriptors) else null + + return nodeProducer(target.toList(), common) +} + +private fun commonize(descriptors: List, commonizer: Commonizer): R? { + for (item in descriptors) { + if (item == null || !commonizer.commonizeWith(item)) + return null + } + + return commonizer.result +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/nodes.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/nodes.kt new file mode 100644 index 00000000000..1afe8f74dc4 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/ir/nodes.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.ir + +interface Declaration + +interface Node { + val target: List + val common: D? + + fun accept(visitor: NodeVisitor, data: T): R +} + +class RootNode( + override val target: List, + override val common: Root +) : Node { + val modules: MutableList = ArrayList() + + override fun accept(visitor: NodeVisitor, data: T): R = + visitor.visitRootNode(this, data) +} + +class ModuleNode( + override val target: List, + override val common: Module? +) : Node { + val packages: MutableList = ArrayList() + + override fun accept(visitor: NodeVisitor, data: T) = + visitor.visitModuleNode(this, data) +} + +class PackageNode( + override val target: List, + override val common: Package? +) : Node { + val properties: MutableList = ArrayList() + + override fun accept(visitor: NodeVisitor, data: T) = + visitor.visitPackageNode(this, data) +} + +class PropertyNode( + override val target: List, + override val common: Property? +) : Node { + override fun accept(visitor: NodeVisitor, data: T) = + visitor.visitPropertyNode(this, data) +} + +internal val Node.indexOfCommon: Int + get() = target.size + +internal val Node.dimension: Int + get() = target.size + 1 diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/modules.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/modules.kt new file mode 100644 index 00000000000..259d2505434 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/modules.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.mergedtree + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroupMap +import org.jetbrains.kotlin.descriptors.commonizer.ir.ModuleNode +import org.jetbrains.kotlin.descriptors.commonizer.ir.buildModuleNode +import org.jetbrains.kotlin.descriptors.commonizer.toList +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.utils.alwaysTrue + +internal fun mergeModules(modules: List): ModuleNode { + val node = buildModuleNode(modules) + + val packageMemberScopesMap = CommonizedGroupMap(modules.size) + + modules.forEachIndexed { index, module -> + module?.collectNonEmptyPackageMemberScopes { packageFqName, memberScope -> + packageMemberScopesMap[packageFqName][index] = memberScope + } + } + + for ((packageFqName, packageMemberScopesGroup) in packageMemberScopesMap) { + node.packages += mergePackages(packageFqName, packageMemberScopesGroup.toList()) + } + + return node +} + +// collects member scopes for every non-empty package provided by this module +private fun ModuleDescriptor.collectNonEmptyPackageMemberScopes(collector: (FqName, MemberScope) -> Unit) { + // we don's need to process fragments from other modules which are the dependencies of this module, so + // let's use the appropriate package fragment provider + val packageFragmentProvider = (this as ModuleDescriptorImpl).packageFragmentProviderForModuleContentWithoutDependencies + + fun recurse(packageFqName: FqName) { + val ownPackageFragments = packageFragmentProvider.getPackageFragments(packageFqName) + val ownPackageMemberScopes = ownPackageFragments.asSequence() + .map { it.getMemberScope() } + .filter { it != MemberScope.Empty } + .toList(ownPackageFragments.size) + + if (ownPackageMemberScopes.isNotEmpty()) { + // don't include subpackages into chained member scope + val memberScope = ChainedMemberScope.create( + "package member scope for $packageFqName in $name", + ownPackageMemberScopes + ) + collector(packageFqName, memberScope) + } + + packageFragmentProvider.getSubPackagesOf(packageFqName, alwaysTrue()).map { recurse(it) } + } + + recurse(FqName.ROOT) +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/packages.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/packages.kt new file mode 100644 index 00000000000..8632d37840d --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/packages.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.mergedtree + +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroupMap +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.descriptors.commonizer.ir.PackageNode +import org.jetbrains.kotlin.descriptors.commonizer.ir.buildPackageNode + +internal fun mergePackages( + packageFqName: FqName, + packageMemberScopes: List +): PackageNode { + val node = buildPackageNode(packageFqName, packageMemberScopes) + + val propertiesMap = CommonizedGroupMap(packageMemberScopes.size) + + packageMemberScopes.forEachIndexed { index, memberScope -> + memberScope?.collectProperties { propertyKey, property -> + propertiesMap[propertyKey][index] = property + } + } + + for ((_, propertiesGroup) in propertiesMap) { + node.properties += mergeProperties(propertiesGroup.toList()) + } + + // FIXME: traverse the rest - functions, classes, typealiases + + return node +} + +private fun MemberScope.collectProperties(collector: (PropertyKey, PropertyDescriptor) -> Unit) { + getContributedDescriptors(DescriptorKindFilter.VARIABLES).asSequence() + .filterIsInstance() + .forEach { property -> + collector(PropertyKey(property), property) + } +} + +private data class PropertyKey( + val name: Name, + val extensionReceiverParameterFqName: FqName? +) { + constructor(property: PropertyDescriptor) : this( + property.name, + property.extensionReceiverParameter?.run { type.constructor.declarationDescriptor!!.fqNameSafe } + ) +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/properties.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/properties.kt new file mode 100644 index 00000000000..75daf4f81ed --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/properties.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.mergedtree + +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.ir.buildPropertyNode + +internal fun mergeProperties(properties: List) = buildPropertyNode(properties) diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/root.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/root.kt new file mode 100644 index 00000000000..7d452714701 --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/mergedtree/root.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.mergedtree + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.CommonizedGroupMap +import org.jetbrains.kotlin.descriptors.commonizer.ConcreteTargetId +import org.jetbrains.kotlin.descriptors.commonizer.ir.RootNode +import org.jetbrains.kotlin.descriptors.commonizer.ir.buildRootNode +import org.jetbrains.kotlin.name.Name + +internal fun mergeRoots(modulesByTargets: List>>): RootNode { + val node = buildRootNode(modulesByTargets.map { it.first }) + + val modulesMap = CommonizedGroupMap(modulesByTargets.size) + + modulesByTargets.forEachIndexed { index, (_, modules) -> + for (module in modules) { + modulesMap[module.name][index] = module + } + } + + for ((_, modulesGroup) in modulesMap) { + node.modules += mergeModules(modulesGroup.toList()) + } + + return node +} diff --git a/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils.kt b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils.kt new file mode 100644 index 00000000000..620d7f0e61d --- /dev/null +++ b/konan/commonizer/src/org/jetbrains/kotlin/descriptors/commonizer/utils.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.isCommon +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance + +internal fun Sequence.toList(expectedCapacity: Int): List { + val result = ArrayList(expectedCapacity) + toCollection(result) + return result +} + +internal inline fun Iterable.firstNonNull() = firstIsInstance() + +internal fun List.asCommonPlatform() = + TargetPlatform(flatMap(TargetPlatform::componentPlatforms).toSet()).also { check(it.isCommon()) } diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_one.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_one.kt new file mode 100644 index 00000000000..77f6432d8e4 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_one.kt @@ -0,0 +1,3 @@ +package org.sample.one + +val firstOrgSampleOne = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_three.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_three.kt new file mode 100644 index 00000000000..70138b3b313 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_three.kt @@ -0,0 +1,3 @@ +package org.sample.three + +val firstOrgSampleThree = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_two.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_two.kt new file mode 100644 index 00000000000..d4ecdc5b75c --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_org_sample_two.kt @@ -0,0 +1,3 @@ +package org.sample.two + +val firstOrgSampleTwo = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_root.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_root.kt new file mode 100644 index 00000000000..55c42e887ae --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/1/package_root.kt @@ -0,0 +1,3 @@ +// root package + +val firstRoot = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_org_sample_one.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_org_sample_one.kt new file mode 100644 index 00000000000..e0e75ac7a7e --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_org_sample_one.kt @@ -0,0 +1,3 @@ +package org.sample.one + +val secondOrgSampleOne = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_org_sample_two.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_org_sample_two.kt new file mode 100644 index 00000000000..884f4c48bd5 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_org_sample_two.kt @@ -0,0 +1,3 @@ +package org.sample.two + +val secondOrgSampleTwo = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_root.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_root.kt new file mode 100644 index 00000000000..b71867a2e87 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/2/package_root.kt @@ -0,0 +1,3 @@ +// root package + +val secondRoot = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_org_sample_three.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_org_sample_three.kt new file mode 100644 index 00000000000..da72b870c8a --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_org_sample_three.kt @@ -0,0 +1,3 @@ +package org.sample.three + +val thirdOrgSampleThree = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_org_sample_two.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_org_sample_two.kt new file mode 100644 index 00000000000..e940cfb0eb6 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_org_sample_two.kt @@ -0,0 +1,3 @@ +package org.sample.two + +val thirdOrgSampleTwo = 1 diff --git a/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_root.kt b/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_root.kt new file mode 100644 index 00000000000..6e6a260569e --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/matchingPackages/3/package_root.kt @@ -0,0 +1,3 @@ +// root package + +val thirdRoot = 1 diff --git a/konan/commonizer/testData/propertyCommonization/sample/1/package_root.kt b/konan/commonizer/testData/propertyCommonization/sample/1/package_root.kt new file mode 100644 index 00000000000..e72e40e4424 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/sample/1/package_root.kt @@ -0,0 +1,21 @@ +// root package + +val foo = 1 + +val bar = 42 +val String.bar get() = this +val Int.bar get() = this + +var baz = "baz" +var bazWithSetter = "baz" + set +var bazWithCustomSetter = "baz" + set(value) { + field = value.toLowerCase() + } +var bazWithSetterWithInternalVisibility = "baz" + internal set +var bazWithCustomSetterWithInternalVisibility = "baz" + internal set(value) { + field = value.toLowerCase() + } diff --git a/konan/commonizer/testData/propertyCommonization/sample/2/package_root.kt b/konan/commonizer/testData/propertyCommonization/sample/2/package_root.kt new file mode 100644 index 00000000000..e72e40e4424 --- /dev/null +++ b/konan/commonizer/testData/propertyCommonization/sample/2/package_root.kt @@ -0,0 +1,21 @@ +// root package + +val foo = 1 + +val bar = 42 +val String.bar get() = this +val Int.bar get() = this + +var baz = "baz" +var bazWithSetter = "baz" + set +var bazWithCustomSetter = "baz" + set(value) { + field = value.toLowerCase() + } +var bazWithSetterWithInternalVisibility = "baz" + internal set +var bazWithCustomSetterWithInternalVisibility = "baz" + internal set(value) { + field = value.toLowerCase() + } diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt new file mode 100644 index 00000000000..cd4ae95fb84 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/AbstractCommonizationFromSourcesTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +import com.intellij.testFramework.PlatformTestUtil.lowercaseFirstLetter +import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.test.KotlinTestUtils.* +import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase +import java.io.File + +@Suppress("MemberVisibilityCanBePrivate") +abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() { + companion object { + init { + System.setProperty("java.awt.headless", "true") + } + + fun List.eachModuleAsTarget() = CommonizationParameters().also { + forEachIndexed { index, module -> + it.addTarget("target_$index", listOf(module)) + } + } + } + + fun assertIsDirectory(file: File) { + assertTrue("Not a directory: $file", file.isDirectory) + } + + protected fun createEnvironment(bareModuleName: String): KotlinCoreEnvironment { + check(Name.isValidIdentifier(bareModuleName)) + val configuration = newConfiguration() + configuration.put(CommonConfigurationKeys.MODULE_NAME, bareModuleName) + + return KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.METADATA_CONFIG_FILES) + } + + protected val KotlinCoreEnvironment.moduleName: Name + get() { + val bareModuleName = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME) + return Name.special("<$bareModuleName>") + } + + protected val testDataDir: File + get() { + val testCaseDir = lowercaseFirstLetter( + this::class.java.simpleName.substringBefore("FromSources").substringBefore("Test"), + true + ) + val testDir = testDirectoryName + + return File(getHomeDirectory()) + .resolve("konan/commonizer/testData") + .resolve(testCaseDir) + .resolve(testDir) + .also(::assertIsDirectory) + } + + protected val sourceModuleRoots: List + get() { + val testDataDir = testDataDir + val roots = testDataDir.listFiles()?.toList() + + if (roots.isNullOrEmpty()) + error("No source module roots found in $testDataDir") + + roots.forEach(::assertIsDirectory) + + return roots + } + + protected val sourceModuleDescriptors: List + get() { + return sourceModuleRoots.map { root -> + val environment = createEnvironment(root.parentFile.name) + val psiFactory = KtPsiFactory(environment.project) + + val psiFiles = root.walkTopDown() + .filter { it.isFile } + .map { psiFactory.createFile(it.name, doLoadFile(it)) } + .toList() + + CommonResolverForModuleFactory.analyzeFiles( + files = psiFiles, + moduleName = environment.moduleName, + dependOnBuiltIns = true, + languageVersionSettings = environment.configuration.languageVersionSettings + ) { content -> + environment.createPackagePartProvider(content.moduleContentScope) + }.moduleDescriptor + } + } +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/BasicCommonizerFacadeTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/BasicCommonizerFacadeTest.kt new file mode 100644 index 00000000000..da787da9c07 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/BasicCommonizerFacadeTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.junit.Assert.* +import org.junit.Test +import org.jetbrains.kotlin.descriptors.commonizer.AbstractCommonizationFromSourcesTest.Companion.eachModuleAsTarget + +class BasicCommonizerFacadeTest { + + @Test + fun nothingToCommonize0() { + val modules = listOf() + + val result = runCommonization(modules.eachModuleAsTarget()) + + assertEquals(NothingToCommonize, result) + } + + @Test + fun nothingToCommonize1() { + val modules = listOf( + mockEmptyModule("") + ) + + val result = runCommonization(modules.eachModuleAsTarget()) + + assertEquals(NothingToCommonize, result) + } + + @Test + fun nothingToCommonize2() { + val modules = listOf( + mockEmptyModule(""), + mockEmptyModule("") + ) + + val result = runCommonization(modules.eachModuleAsTarget()) + + assertTrue(result is CommonizationPerformed) + require(result is CommonizationPerformed) // to enforce Kotlin contracts + + assertSingleModuleForTarget("", result.commonModules) + + assertEquals(2, result.modulesByTargets.size) + for (modulesSamePlatform in result.modulesByTargets.values) { + assertSingleModuleForTarget("", modulesSamePlatform) + } + } + + @Test + fun mismatchedModules() { + val modules = listOf( + mockEmptyModule(""), + mockEmptyModule(""), + mockEmptyModule("") + ) + + val result = runCommonization(modules.eachModuleAsTarget()) + + assertEquals(NothingToCommonize, result) + } + + private fun assertSingleModuleForTarget( + @Suppress("SameParameterValue") expectedModuleName: String, + modules: Collection + ) { + assertEquals(1, modules.size) + assertEquals(expectedModuleName, modules.single().name.asString()) + } +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/PropertyCommonizationFromSourcesTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/PropertyCommonizationFromSourcesTest.kt new file mode 100644 index 00000000000..f530f602163 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/PropertyCommonizationFromSourcesTest.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +class PropertyCommonizationFromSourcesTest : AbstractCommonizationFromSourcesTest() { + fun testMatchingPackages() { + val modules = sourceModuleDescriptors + val result = runCommonization(modules.eachModuleAsTarget()) + + println(result) + + // TODO: implement + } + + fun testSample() { + val modules = sourceModuleDescriptors + runCommonization(modules.eachModuleAsTarget()) + } +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractCommonizerTest.kt new file mode 100644 index 00000000000..b85ecea4363 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/AbstractCommonizerTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.junit.Assert.* +import org.junit.Test + +abstract class AbstractCommonizerTest { + + @Test(expected = IllegalStateException::class) + fun failOnNoVariantsSubmitted() { + createCommonizer().result + fail() + } + + protected abstract fun createCommonizer(): Commonizer + + protected open fun isEqual(a: R?, b: R?): Boolean = a == b + + protected fun doTestSuccess(expected: R, vararg variants: T) { + check(variants.isNotEmpty()) + + val commonized = createCommonizer().apply { + variants.forEach { + assertTrue(commonizeWith(it)) + } + } + + val actual = commonized.result + if (!isEqual(expected, actual)) fail("Expected: $expected\nActual: $actual") + } + + // should fail on the last variant + protected fun doTestFailure(vararg variants: T) { + check(variants.isNotEmpty()) + + val commonized = createCommonizer().apply { + variants.forEachIndexed { index, variant -> + val result = commonizeWith(variant) + if (index == variants.size - 1) assertFalse(result) else assertTrue(result) + } + } + + commonized.result + fail() + } +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultExtensionReceiverCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultExtensionReceiverCommonizerTest.kt new file mode 100644 index 00000000000..b822fdc8408 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultExtensionReceiverCommonizerTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.descriptors.commonizer.mockClassType +import org.jetbrains.kotlin.descriptors.commonizer.mockProperty +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.refinement.TypeRefinement +import org.junit.Test + +@TypeRefinement +class DefaultExtensionReceiverCommonizerTest : AbstractCommonizerTest() { + + @Test + fun nullReceiver() = doTestSuccess( + null, + mock().extensionReceiverParameter, + mock().extensionReceiverParameter, + mock().extensionReceiverParameter + ) + + @Test + fun sameReceiver() = doTestSuccess( + mockClassType("kotlin.String").unwrap(), + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter, + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter, + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter + ) + + @Test(expected = IllegalStateException::class) + fun differentReceivers() = doTestFailure( + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter, + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter, + mock(receiverTypeFqName = "kotlin.Int").extensionReceiverParameter + ) + + @Test(expected = IllegalStateException::class) + fun nullAndNonNullReceivers1() = doTestFailure( + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter, + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter, + mock(receiverTypeFqName = null).extensionReceiverParameter + ) + + @Test(expected = IllegalStateException::class) + fun nullAndNonNullReceivers2() = doTestFailure( + mock(receiverTypeFqName = null).extensionReceiverParameter, + mock(receiverTypeFqName = null).extensionReceiverParameter, + mock(receiverTypeFqName = "kotlin.String").extensionReceiverParameter + ) + + override fun createCommonizer() = ExtensionReceiverCommonizer.default() +} + +@TypeRefinement +private fun mock(name: String = "myLength", receiverTypeFqName: String? = null) = mockProperty( + name = name, + setterVisibility = null, + extensionReceiverType = receiverTypeFqName?.let { mockClassType(receiverTypeFqName) }, + returnType = mockClassType("kotlin.Int") +) diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultModalityCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultModalityCommonizerTest.kt new file mode 100644 index 00000000000..8c02312b3ad --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultModalityCommonizerTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Modality.* +import org.junit.Test + +class DefaultModalityCommonizerTest : AbstractCommonizerTest() { + + @Test + fun onlyFinal() = doTestSuccess(FINAL, FINAL, FINAL, FINAL) + + @Test + fun onlyOpen() = doTestSuccess(OPEN, OPEN, OPEN, OPEN) + + @Test + fun onlySealed() = doTestSuccess(SEALED, SEALED, SEALED, SEALED) + + @Test + fun onlyAbstract() = doTestSuccess(ABSTRACT, ABSTRACT, ABSTRACT, ABSTRACT) + + @Test(expected = IllegalStateException::class) + fun sealedAndAbstract() = doTestFailure(SEALED, ABSTRACT) + + @Test(expected = IllegalStateException::class) + fun sealedAndFinal() = doTestFailure(SEALED, FINAL) + + @Test(expected = IllegalStateException::class) + fun abstractAndFinal() = doTestFailure(ABSTRACT, FINAL) + + @Test + fun finalAndOpen() = doTestSuccess(FINAL, FINAL, OPEN, FINAL) + + @Test + fun openAndFinal() = doTestSuccess(FINAL, OPEN, OPEN, FINAL) + + override fun createCommonizer() = ModalityCommonizer.default() +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultPropertySetterCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultPropertySetterCommonizerTest.kt new file mode 100644 index 00000000000..d7c907efe21 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultPropertySetterCommonizerTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities.* +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.descriptors.commonizer.ir.Setter +import org.jetbrains.kotlin.descriptors.commonizer.mockClassType +import org.jetbrains.kotlin.descriptors.commonizer.mockProperty +import org.jetbrains.kotlin.types.refinement.TypeRefinement +import org.junit.Test + +class DefaultPropertySetterCommonizerTest : AbstractCommonizerTest() { + + @Test + fun absentOnly() = super.doTestSuccess(null, null, null, null) + + @Test(expected = IllegalStateException::class) + fun absentAndPublic() = doTestFailure(null, null, null, PUBLIC) + + @Test(expected = IllegalStateException::class) + fun publicAndAbsent() = doTestFailure(PUBLIC, PUBLIC, PUBLIC, null) + + @Test(expected = IllegalStateException::class) + fun protectedAndAbsent() = doTestFailure(PROTECTED, PROTECTED, null) + + @Test(expected = IllegalStateException::class) + fun absentAndInternal() = doTestFailure(null, null, INTERNAL) + + @Test + fun publicOnly() = doTestSuccess(PUBLIC, PUBLIC, PUBLIC, PUBLIC) + + @Test + fun protectedOnly() = doTestSuccess(PROTECTED, PROTECTED, PROTECTED, PROTECTED) + + @Test + fun internalOnly() = doTestSuccess(INTERNAL, INTERNAL, INTERNAL, INTERNAL) + + @Test(expected = IllegalStateException::class) + fun privateOnly() = doTestFailure(PRIVATE) + + @Test + fun publicAndProtected() = doTestSuccess(PROTECTED, PUBLIC, PROTECTED, PUBLIC) + + @Test + fun publicAndInternal() = doTestSuccess(INTERNAL, PUBLIC, INTERNAL, PUBLIC) + + @Test(expected = IllegalStateException::class) + fun protectedAndInternal() = doTestFailure(PUBLIC, INTERNAL, PROTECTED) + + @Test(expected = IllegalStateException::class) + fun publicAndPrivate() = doTestFailure(PUBLIC, INTERNAL, PRIVATE) + + @Test(expected = IllegalStateException::class) + fun somethingUnexpected() = doTestFailure(PUBLIC, LOCAL) + + private fun doTestSuccess(expected: Visibility?, vararg variants: Visibility?) = + super.doTestSuccess( + expected?.let { Setter.createDefaultNoAnnotations(expected) }, + *variants.map { it?.let(Visibility::toMockProperty) }.toTypedArray() + ) + + private fun doTestFailure(vararg variants: Visibility?) = + super.doTestFailure( + *variants.map { it?.let(Visibility::toMockProperty) }.toTypedArray() + ) + + override fun createCommonizer() = PropertySetterCommonizer.default() +} + + +@UseExperimental(TypeRefinement::class) +private fun Visibility.toMockProperty() = mockProperty( + name = "myProperty", + setterVisibility = this, + extensionReceiverType = null, + returnType = mockClassType("kotlin.String").unwrap() +).setter!! diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultTypeCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultTypeCommonizerTest.kt new file mode 100644 index 00000000000..0e62553a0cd --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/DefaultTypeCommonizerTest.kt @@ -0,0 +1,363 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.commonizer.mockClassType +import org.jetbrains.kotlin.descriptors.commonizer.mockTAType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.refinement.TypeRefinement +import org.junit.Test + +// TODO: add tests for type parameters +@TypeRefinement +class DefaultTypeCommonizerTest : AbstractCommonizerTest() { + + @Test + fun classTypesInKotlinPackageWithSameName() = doTestSuccess( + mockClassType("kotlin.collections.List").unwrap(), + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.collections.List") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinPackageWithDifferentNames1() = doTestFailure( + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.fictitiousPackageName.List") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinPackageWithDifferentNames2() = doTestFailure( + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.collections.Set") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinPackageWithDifferentNames3() = doTestFailure( + mockClassType("kotlin.collections.List"), + mockClassType("kotlin.collections.List"), + mockClassType("org.sample.Foo") + ) + + @Test + fun classTypesInKotlinxPackageWithSameName() = doTestSuccess( + mockClassType("kotlinx.cinterop.CPointer").unwrap(), + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.cinterop.CPointer") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinxPackageWithDifferentNames1() = doTestFailure( + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.fictitiousPackageName.CPointer") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinxPackageWithDifferentNames2() = doTestFailure( + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.cinterop.ObjCObject") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinxPackageWithDifferentNames3() = doTestFailure( + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("kotlinx.cinterop.CPointer"), + mockClassType("org.sample.Foo") + ) + + @Test + fun classTypesInUserPackageWithSameName() = doTestSuccess( + mockClassType("org.sample.Foo").unwrap(), + mockClassType("org.sample.Foo"), + mockClassType("org.sample.Foo"), + mockClassType("org.sample.Foo") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInUserPackageWithDifferentNames1() = doTestFailure( + mockClassType("org.sample.Foo"), + mockClassType("org.sample.Foo"), + mockClassType("org.fictitiousPackageName.Foo") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInUserPackageWithDifferentNames2() = doTestFailure( + mockClassType("org.sample.Foo"), + mockClassType("org.sample.Foo"), + mockClassType("org.sample.Bar") + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInUserPackageWithDifferentNames3() = doTestFailure( + mockClassType("org.sample.Foo"), + mockClassType("org.sample.Foo"), + mockClassType("kotlin.String") + ) + + @Test + fun classTypesInKotlinPackageWithSameNullability1() = doTestSuccess( + mockClassType("kotlin.collections.List").unwrap(), + mockClassType("kotlin.collections.List", nullable = false), + mockClassType("kotlin.collections.List", nullable = false), + mockClassType("kotlin.collections.List", nullable = false) + ) + + @Test + fun classTypesInKotlinPackageWithSameNullability2() = doTestSuccess( + mockClassType("kotlin.collections.List", nullable = true).unwrap(), + mockClassType("kotlin.collections.List", nullable = true), + mockClassType("kotlin.collections.List", nullable = true), + mockClassType("kotlin.collections.List", nullable = true) + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinPackageWithDifferentNullability1() = doTestFailure( + mockClassType("kotlin.collections.List", nullable = false), + mockClassType("kotlin.collections.List", nullable = false), + mockClassType("kotlin.collections.List", nullable = true) + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInKotlinPackageWithDifferentNullability2() = doTestFailure( + mockClassType("kotlin.collections.List", nullable = true), + mockClassType("kotlin.collections.List", nullable = true), + mockClassType("kotlin.collections.List", nullable = false) + ) + + @Test + fun classTypesInUserPackageWithSameNullability1() = doTestSuccess( + mockClassType("org.sample.Foo").unwrap(), + mockClassType("org.sample.Foo", nullable = false), + mockClassType("org.sample.Foo", nullable = false), + mockClassType("org.sample.Foo", nullable = false) + ) + + @Test + fun classTypesInUserPackageWithSameNullability2() = doTestSuccess( + mockClassType("org.sample.Foo", nullable = true).unwrap(), + mockClassType("org.sample.Foo", nullable = true), + mockClassType("org.sample.Foo", nullable = true), + mockClassType("org.sample.Foo", nullable = true) + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInUserPackageWithDifferentNullability1() = doTestFailure( + mockClassType("org.sample.Foo", nullable = false), + mockClassType("org.sample.Foo", nullable = false), + mockClassType("org.sample.Foo", nullable = true) + ) + + @Test(expected = IllegalStateException::class) + fun classTypesInUserPackageWithDifferentNullability2() = doTestFailure( + mockClassType("org.sample.Foo", nullable = true), + mockClassType("org.sample.Foo", nullable = true), + mockClassType("org.sample.Foo", nullable = false) + ) + + @Test + fun taTypesInKotlinPackageWithSameNameAndClass() = doTestSuccess( + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }.unwrap(), + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInKotlinPackageWithDifferentNames() = doTestFailure( + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.FictitiousTypeAlias") { mockClassType("kotlin.sequences.SequenceScope") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInKotlinPackageWithDifferentClasses() = doTestFailure( + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.FictitiousClass") } + ) + + @Test + fun taTypesInKotlinxPackageWithSameNameAndClass() = doTestSuccess( + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }.unwrap(), + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }, + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }, + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInKotlinxPackageWithDifferentNames() = doTestFailure( + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }, + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }, + mockTAType("kotlinx.cinterop.FictitiousTypeAlias") { mockClassType("kotlinx.cinterop.CPointer") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInKotlinxPackageWithDifferentClasses() = doTestFailure( + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }, + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.CPointer") }, + mockTAType("kotlinx.cinterop.CArrayPointer") { mockClassType("kotlinx.cinterop.FictitiousClass") } + ) + + @Test + fun multilevelTATypesInKotlinPackageWithSameNameAndRightHandSideClass() = doTestSuccess( + // that's OK as long as the fully expanded right-hand side is the same class + mockTAType("kotlin.FictitiousTypeAlias") { + mockClassType("kotlin.FictitiousClass") + }.unwrap(), + + mockTAType("kotlin.FictitiousTypeAlias") { + mockClassType("kotlin.FictitiousClass") + }, + + mockTAType("kotlin.FictitiousTypeAlias") { + mockTAType("kotlin.FictitiousTypeAliasL2") { + mockClassType("kotlin.FictitiousClass") + } + }, + + mockTAType("kotlin.FictitiousTypeAlias") { + mockTAType("kotlin.FictitiousTypeAliasL2") { + mockTAType("kotlin.FictitiousTypeAliasL3") { + mockClassType("kotlin.FictitiousClass") + } + } + } + ) + + @Test + fun taTypesInUserPackageWithSameNameAndClass() = doTestSuccess( + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }.unwrap(), + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInUserPackageWithDifferentNames() = doTestFailure( + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.BarAlias") { mockClassType("org.sample.Foo") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInUserPackageWithDifferentClasses() = doTestFailure( + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Bar") } + ) + + @Test(expected = IllegalStateException::class) + fun multilevelTATypesInUserPackageWithSameNameAndRightHandSideClass() = doTestFailure( + mockTAType("org.sample.FooAlias") { + mockClassType("org.sample.Foo") + }, + + mockTAType("org.sample.FooAlias") { + mockTAType("org.sample.FooAliasL2") { + mockClassType("org.sample.Foo") + } + } + ) + + @Test + fun taTypesInKotlinPackageWithSameNullability1() = doTestSuccess( + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }.unwrap(), + mockTAType("kotlin.sequences.SequenceBuilder", nullable = false) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = false) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = false) { mockClassType("kotlin.sequences.SequenceScope") } + ) + + @Test + fun taTypesInKotlinPackageWithSameNullability2() = doTestSuccess( + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") }.unwrap(), + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInKotlinPackageWithDifferentNullability1() = doTestFailure( + mockTAType("kotlin.sequences.SequenceBuilder", nullable = false) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = false) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInKotlinPackageWithDifferentNullability2() = doTestFailure( + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = true) { mockClassType("kotlin.sequences.SequenceScope") }, + mockTAType("kotlin.sequences.SequenceBuilder", nullable = false) { mockClassType("kotlin.sequences.SequenceScope") } + ) + + @Test + fun taTypesInKotlinPackageWithDifferentNullability3() = doTestSuccess( + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }.unwrap(), + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope", nullable = false) }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope", nullable = false) }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope", nullable = true) } + ) + + @Test + fun taTypesInKotlinPackageWithDifferentNullability4() = doTestSuccess( + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope") }.unwrap(), + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope", nullable = true) }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope", nullable = true) }, + mockTAType("kotlin.sequences.SequenceBuilder") { mockClassType("kotlin.sequences.SequenceScope", nullable = false) } + ) + + @Test + fun taTypesInUserPackageWithSameNullability1() = doTestSuccess( + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo") }.unwrap(), + mockTAType("org.sample.FooAlias", nullable = false) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = false) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = false) { mockClassType("org.sample.Foo") } + ) + + @Test + fun taTypesInUserPackageWithSameNullability2() = doTestSuccess( + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") }.unwrap(), + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInUserPackageWithDifferentNullability1() = doTestFailure( + mockTAType("org.sample.FooAlias", nullable = false) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = false) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInUserPackageWithDifferentNullability2() = doTestFailure( + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = true) { mockClassType("org.sample.Foo") }, + mockTAType("org.sample.FooAlias", nullable = false) { mockClassType("org.sample.Foo") } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInUserPackageWithDifferentNullability3() = doTestFailure( + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo", nullable = false) }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo", nullable = false) }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo", nullable = true) } + ) + + @Test(expected = IllegalStateException::class) + fun taTypesInUserPackageWithDifferentNullability4() = doTestFailure( + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo", nullable = true) }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo", nullable = true) }, + mockTAType("org.sample.FooAlias") { mockClassType("org.sample.Foo", nullable = false) } + ) + + override fun createCommonizer() = TypeCommonizer.default() + override fun isEqual(a: UnwrappedType?, b: UnwrappedType?) = (a === b) || (a != null && b != null && areTypesEqual(a, b)) +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/EqualizingVisibilityCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/EqualizingVisibilityCommonizerTest.kt new file mode 100644 index 00000000000..a3bb3ed5ae0 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/EqualizingVisibilityCommonizerTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.Visibilities.* +import org.jetbrains.kotlin.descriptors.Visibility +import org.junit.Test + +class EqualizingVisibilityCommonizerTest : AbstractCommonizerTest() { + + @Test + fun publicOnly() = doTestSuccess(PUBLIC, PUBLIC, PUBLIC, PUBLIC) + + @Test + fun protectedOnly() = doTestSuccess(PROTECTED, PROTECTED, PROTECTED, PROTECTED) + + @Test + fun internalOnly() = doTestSuccess(INTERNAL, INTERNAL, INTERNAL, INTERNAL) + + @Test(expected = IllegalStateException::class) + fun privateOnly() = doTestFailure(PRIVATE) + + @Test(expected = IllegalStateException::class) + fun publicAndProtected() = doTestFailure(PROTECTED, PROTECTED, PROTECTED, PUBLIC) + + @Test(expected = IllegalStateException::class) + fun publicAndInternal() = doTestFailure(INTERNAL, INTERNAL, INTERNAL, PUBLIC) + + @Test(expected = IllegalStateException::class) + fun protectedAndInternal() = doTestFailure(PROTECTED, PROTECTED, PROTECTED, INTERNAL) + + @Test(expected = IllegalStateException::class) + fun publicAndPrivate() = doTestFailure(PUBLIC, PUBLIC, PRIVATE) + + @Test(expected = IllegalStateException::class) + fun somethingUnexpected() = doTestFailure(PUBLIC, LOCAL) + + override fun createCommonizer() = VisibilityCommonizer.equalizing() +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/LoweringVisibilityCommonizerTest.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/LoweringVisibilityCommonizerTest.kt new file mode 100644 index 00000000000..0d436f6dcf6 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/core/LoweringVisibilityCommonizerTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer.core + +import org.jetbrains.kotlin.descriptors.Visibilities.* +import org.jetbrains.kotlin.descriptors.Visibility +import org.junit.Test + +class LoweringVisibilityCommonizerTest : AbstractCommonizerTest() { + + @Test + fun publicOnly() = doTestSuccess(PUBLIC, PUBLIC, PUBLIC, PUBLIC) + + @Test + fun protectedOnly() = doTestSuccess(PROTECTED, PROTECTED, PROTECTED, PROTECTED) + + @Test + fun internalOnly() = doTestSuccess(INTERNAL, INTERNAL, INTERNAL, INTERNAL) + + @Test(expected = IllegalStateException::class) + fun privateOnly() = doTestFailure(PRIVATE) + + @Test + fun publicAndProtected() = doTestSuccess(PROTECTED, PUBLIC, PROTECTED, PUBLIC) + + @Test + fun publicAndInternal() = doTestSuccess(INTERNAL, PUBLIC, INTERNAL, PUBLIC) + + @Test(expected = IllegalStateException::class) + fun protectedAndInternal() = doTestFailure(PUBLIC, INTERNAL, PROTECTED) + + @Test(expected = IllegalStateException::class) + fun publicAndPrivate() = doTestFailure(PUBLIC, INTERNAL, PRIVATE) + + @Test(expected = IllegalStateException::class) + fun somethingUnexpected() = doTestFailure(PUBLIC, LOCAL) + + override fun createCommonizer() = VisibilityCommonizer.lowering() +} diff --git a/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/mocks.kt b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/mocks.kt new file mode 100644 index 00000000000..d584f2aeac8 --- /dev/null +++ b/konan/commonizer/tests/org/jetbrains/kotlin/descriptors/commonizer/mocks.kt @@ -0,0 +1,229 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.commonizer + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.parentOrNull +import org.jetbrains.kotlin.resolve.DescriptorFactory +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.storage.LockBasedStorageManager +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.checker.KotlinTypeRefiner +import org.jetbrains.kotlin.types.refinement.TypeRefinement +import kotlin.random.Random + +// expected special name for module +internal fun mockEmptyModule(moduleName: String): ModuleDescriptor { + val module = KotlinTestUtils.createEmptyModule(moduleName) + module.initialize(PackageFragmentProvider.Empty) + return module +} + +@TypeRefinement +internal fun mockClassType( + fqName: String, + nullable: Boolean = false +): KotlinType = LazyWrappedType(LockBasedStorageManager.NO_LOCKS) { + val classFqName = FqName(fqName) + + val classTypeConstructor = object : AbstractClassTypeConstructor(LockBasedStorageManager.NO_LOCKS) { + lateinit var classDescriptor: ClassDescriptor + override fun getParameters(): List = emptyList() + override fun computeSupertypes(): List = emptyList() + override fun isDenotable() = true + override fun getDeclarationDescriptor() = classDescriptor + override val supertypeLoopChecker = SupertypeLoopChecker.EMPTY + override fun toString() = "class type constructor ${declarationDescriptor.name}" + } + + val classDescriptor = object : ClassDescriptorBase( + /*storageManager =*/ LockBasedStorageManager.NO_LOCKS, + /*containingDeclaration =*/ createPackageFragmentForClassifier(classFqName), + /*name =*/ classFqName.shortName(), + /*source =*/ SourceElement.NO_SOURCE, + /*isExternal =*/ false + ) { + override fun getStaticScope() = MemberScope.Empty + override fun getConstructors(): List = emptyList() + override fun getCompanionObjectDescriptor(): ClassDescriptor? = null + override fun getKind() = ClassKind.CLASS + override fun getModality() = Modality.FINAL + override fun getVisibility() = Visibilities.PUBLIC + override fun isCompanionObject() = false + override fun isData() = false + override fun isInline() = false + override fun getUnsubstitutedPrimaryConstructor(): ClassConstructorDescriptor? = null + override fun isExpect() = false + override fun getUnsubstitutedMemberScope(kotlinTypeRefiner: KotlinTypeRefiner) = MemberScope.Empty + override fun isActual() = false + override fun getSealedSubclasses(): List = emptyList() + override fun getTypeConstructor() = classTypeConstructor + override fun isInner() = false + override fun getDeclaredTypeParameters(): List = emptyList() + override val annotations = Annotations.EMPTY + override fun toString() = "class descriptor $name" + } + + classTypeConstructor.classDescriptor = classDescriptor + + createSimpleType(classTypeConstructor, nullable) +} + +@TypeRefinement +internal fun mockTAType( + fqName: String, + nullable: Boolean = false, + rightHandSideTypeProvider: () -> KotlinType +): KotlinType = LazyWrappedType(LockBasedStorageManager.NO_LOCKS) { + val typeAliasFqName = FqName(fqName) + + val rightHandSideType = rightHandSideTypeProvider().lowerIfFlexible() + + val typeAliasDescriptor = object : AbstractTypeAliasDescriptor( + containingDeclaration = createPackageFragmentForClassifier(typeAliasFqName), + annotations = Annotations.EMPTY, + name = typeAliasFqName.shortName(), + sourceElement = SourceElement.NO_SOURCE, + visibilityImpl = Visibilities.PUBLIC + ) { + private val myDefaultType by lazy { createSimpleType(typeConstructor, nullable) } + override val storageManager = LockBasedStorageManager.NO_LOCKS + override fun getTypeConstructorTypeParameters(): List = emptyList() + override val underlyingType by lazy { rightHandSideType.getAbbreviation() ?: rightHandSideType } + override fun getDefaultType() = myDefaultType + override val classDescriptor get() = expandedType.constructor.declarationDescriptor as ClassDescriptor? + override val constructors: Collection = emptyList() + override fun substitute(substitutor: TypeSubstitutor) = this + override val expandedType by lazy { rightHandSideType } + } + + (rightHandSideType.getAbbreviatedType()?.expandedType ?: rightHandSideType).withAbbreviation(typeAliasDescriptor.defaultType) +} + +internal fun mockProperty( + name: String, + setterVisibility: Visibility?, + extensionReceiverType: KotlinType?, + returnType: KotlinType +): PropertyDescriptor { + val propertyName = Name.identifier(name) + + val containingDeclaration = object : DeclarationDescriptorImpl(Annotations.EMPTY, Name.special("")) { + override fun getContainingDeclaration() = error("not supported") + override fun accept(visitor: DeclarationDescriptorVisitor?, data: D) = error("not supported") + } + + val propertyDescriptor = PropertyDescriptorImpl.create( + /*containingDeclaration =*/ containingDeclaration, + /*annotations =*/ Annotations.EMPTY, + /*modality =*/ Modality.FINAL, + /*visibility =*/ Visibilities.PUBLIC, + /*isVar =*/ setterVisibility != null, + /*name =*/ propertyName, + /*kind =*/ CallableMemberDescriptor.Kind.DECLARATION, + /*source =*/ SourceElement.NO_SOURCE, + /*lateInit =*/ false, + /*isConst =*/ false, + /*isExpect =*/ false, + /*isActual =*/ false, + /*isExternal =*/ false, + /*isDelegated =*/ false + ) + + val extensionReceiverDescriptor = DescriptorFactory.createExtensionReceiverParameterForCallable( + /*owner =*/ propertyDescriptor, + /*receiverParameterType =*/ extensionReceiverType, + /*annotations =*/ Annotations.EMPTY + ) + + val dispatchReceiverDescriptor = DescriptorUtils.getDispatchReceiverParameterIfNeeded(containingDeclaration) + + propertyDescriptor.setType( + /*outType =*/ returnType, + /*typeParameters =*/ emptyList(), + /*dispatchReceiverParameter =*/ dispatchReceiverDescriptor, + /*extensionReceiverParameter =*/ extensionReceiverDescriptor + ) + + val getter = DescriptorFactory.createDefaultGetter( + /*propertyDescriptor =*/ propertyDescriptor, + /*annotations =*/ Annotations.EMPTY + ).apply { + initialize(null) // use return type from the property descriptor + } + + val setter = setterVisibility?.let { + DescriptorFactory.createSetter( + /*propertyDescriptor =*/ propertyDescriptor, + /*annotations =*/ Annotations.EMPTY, + /*parameterAnnotations =*/ Annotations.EMPTY, + /*isDefault =*/ false, + /*isExternal =*/ false, + /*isInline =*/ false, + /*visibility =*/ setterVisibility, + /*sourceElement =*/ SourceElement.NO_SOURCE + ) + } + + propertyDescriptor.initialize(getter, setter) + + return propertyDescriptor +} + +//private fun mockTypeParameterType( +// name: String, +// containingDeclaration: DeclarationDescriptor, +// definitelyNotNull: Boolean = false +//): KotlinType { +// val typeParameterName = Name.identifier(name) +// +// val typeParameterDescriptor = TypeParameterDescriptorImpl.createWithDefaultBound( +// /*containingDeclaration =*/ containingDeclaration, +// /*annotations =*/ Annotations.EMPTY, +// /*reified =*/ false, +// /*variance =*/ Variance.INVARIANT, +// /*name =*/ typeParameterName, +// /*index =*/ 0 +// ) +// +// val simpleType = createSimpleType(typeParameterDescriptor.typeConstructor, false) +// +// return if (definitelyNotNull) +// simpleType.makeSimpleTypeDefinitelyNotNullOrNotNull().also { check(it.isDefinitelyNotNullType) } +// else +// simpleType +//} + +private fun createPackageFragmentForClassifier(classifierFqName: FqName): PackageFragmentDescriptor = + object : PackageFragmentDescriptor { + private val module: ModuleDescriptor by lazy { mockEmptyModule("") } + override fun getContainingDeclaration(): ModuleDescriptor = module + override val fqName = classifierFqName.parentOrNull() ?: FqName.ROOT + override fun getMemberScope() = MemberScope.Empty + override fun getOriginal() = this + override fun getName() = fqName.shortNameOrSpecial() + override fun getSource() = SourceElement.NO_SOURCE + override val annotations = Annotations.EMPTY + override fun accept(visitor: DeclarationDescriptorVisitor?, data: D): R = error("not supported") + override fun acceptVoid(visitor: DeclarationDescriptorVisitor?) = error("not supported") + override fun toString() = "package $name" + } + +private fun createSimpleType(typeConstructor: TypeConstructor, nullable: Boolean): SimpleType = + KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope( + annotations = Annotations.EMPTY, + constructor = typeConstructor, + arguments = emptyList(), + nullable = nullable, + memberScope = MemberScope.Empty, + refinedTypeFactory = { null } + ) diff --git a/settings.gradle b/settings.gradle index 7b846a99455..341729fdfdf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -83,6 +83,7 @@ include ":kotlin-build-common", ":js:js.tests", ":kotlin-native:kotlin-native-utils", ":kotlin-native:kotlin-native-library-reader", + ":kotlin-native:commonizer", ":jps-plugin", ":kotlin-jps-plugin", ":core:descriptors", @@ -337,6 +338,7 @@ project(':kotlin-util-io').projectDir = "$rootDir/compiler/util-io" as File project(':kotlin-util-klib').projectDir = "$rootDir/compiler/util-klib" as File project(':kotlin-native:kotlin-native-utils').projectDir = "$rootDir/konan/utils" as File project(':kotlin-native:kotlin-native-library-reader').projectDir = "$rootDir/konan/library-reader" as File +project(':kotlin-native:commonizer').projectDir = "$rootDir/konan/commonizer" 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