[K/JS] fix(TS): save type hierarchy for exported classes.
This commit is contained in:
Generated
-3
@@ -8,7 +8,4 @@
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.0-dev-1006" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -70,7 +70,7 @@ sealed class ExportedClass : ExportedDeclaration() {
|
||||
abstract val name: String
|
||||
abstract val ir: IrClass
|
||||
abstract val members: List<ExportedDeclaration>
|
||||
abstract val superClass: ExportedType?
|
||||
abstract val superClasses: List<ExportedType>
|
||||
abstract val superInterfaces: List<ExportedType>
|
||||
abstract val nestedClasses: List<ExportedClass>
|
||||
}
|
||||
@@ -79,7 +79,7 @@ data class ExportedRegularClass(
|
||||
override val name: String,
|
||||
val isInterface: Boolean = false,
|
||||
val isAbstract: Boolean = false,
|
||||
override val superClass: ExportedType? = null,
|
||||
override val superClasses: List<ExportedType> = emptyList(),
|
||||
override val superInterfaces: List<ExportedType> = emptyList(),
|
||||
val typeParameters: List<ExportedType.TypeParameter>,
|
||||
override val members: List<ExportedDeclaration>,
|
||||
@@ -89,7 +89,7 @@ data class ExportedRegularClass(
|
||||
|
||||
data class ExportedObject(
|
||||
override val name: String,
|
||||
override val superClass: ExportedType? = null,
|
||||
override val superClasses: List<ExportedType> = emptyList(),
|
||||
override val superInterfaces: List<ExportedType> = emptyList(),
|
||||
override val members: List<ExportedDeclaration>,
|
||||
override val nestedClasses: List<ExportedClass>,
|
||||
@@ -115,6 +115,7 @@ sealed class ExportedType {
|
||||
object String : Primitive("string")
|
||||
object Throwable : Primitive("Error")
|
||||
object Any : Primitive("any")
|
||||
object Unknown : Primitive("unknown")
|
||||
object Unit : Primitive("void")
|
||||
object Nothing : Primitive("never")
|
||||
object UniqueSymbol : Primitive("unique symbol")
|
||||
@@ -147,16 +148,16 @@ sealed class ExportedType {
|
||||
|
||||
class PropertyType(val container: ExportedType, val propertyName: ExportedType) : ExportedType()
|
||||
|
||||
class ImplicitlyExportedType(val type: ExportedType) : ExportedType() {
|
||||
data class ImplicitlyExportedType(val type: ExportedType, val exportedSupertype: ExportedType) : ExportedType() {
|
||||
override fun withNullability(nullable: Boolean) =
|
||||
ImplicitlyExportedType(type.withNullability(nullable))
|
||||
ImplicitlyExportedType(type.withNullability(nullable), exportedSupertype.withNullability(nullable))
|
||||
}
|
||||
|
||||
open fun withNullability(nullable: Boolean) =
|
||||
if (nullable) Nullable(this) else this
|
||||
|
||||
fun withImplicitlyExported(implicitlyExportedType: Boolean) =
|
||||
if (implicitlyExportedType) ImplicitlyExportedType(this) else this
|
||||
fun withImplicitlyExported(implicitlyExportedType: Boolean, exportedSupertype: ExportedType) =
|
||||
if (implicitlyExportedType) ImplicitlyExportedType(this, exportedSupertype) else this
|
||||
}
|
||||
|
||||
enum class ExportedVisibility(val keyword: String) {
|
||||
|
||||
+61
-40
@@ -24,14 +24,14 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
|
||||
private const val magicPropertyName = "__doNotUseOrImplementIt"
|
||||
|
||||
class ExportModelGenerator(
|
||||
val context: JsIrBackendContext,
|
||||
val generateNamespacesForPackages: Boolean
|
||||
) {
|
||||
class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespacesForPackages: Boolean) {
|
||||
private val transitiveExportCollector = TransitiveExportCollector(context)
|
||||
|
||||
fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
||||
val namespaceFqName = file.fqName
|
||||
val exports = file.declarations.flatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
|
||||
@@ -64,10 +64,14 @@ class ExportModelGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportClass(candidate: IrClass) = if (candidate.isEnumClass) {
|
||||
exportEnumClass(candidate)
|
||||
} else {
|
||||
exportOrdinaryClass(candidate)
|
||||
private fun exportClass(candidate: IrClass): ExportedDeclaration? {
|
||||
val superTypes = candidate.defaultType.collectSuperTransitiveHierarchy() + candidate.superTypes
|
||||
|
||||
return if (candidate.isEnumClass) {
|
||||
exportEnumClass(candidate, superTypes)
|
||||
} else {
|
||||
exportOrdinaryClass(candidate, superTypes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? {
|
||||
@@ -203,9 +207,7 @@ class ExportModelGenerator(
|
||||
return Exportability.Allowed
|
||||
}
|
||||
|
||||
private fun exportOrdinaryClass(
|
||||
klass: IrClass
|
||||
): ExportedDeclaration? {
|
||||
private fun exportOrdinaryClass(klass: IrClass, superTypes: Iterable<IrType>): ExportedDeclaration? {
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> error(exportability.reason)
|
||||
is Exportability.NotNeeded -> return null
|
||||
@@ -213,18 +215,17 @@ class ExportModelGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
val (members, nestedClasses) = exportClassDeclarations(klass)
|
||||
val (members, nestedClasses) = exportClassDeclarations(klass, superTypes)
|
||||
|
||||
return exportClass(
|
||||
klass,
|
||||
superTypes,
|
||||
members,
|
||||
nestedClasses
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportEnumClass(
|
||||
klass: IrClass
|
||||
): ExportedDeclaration? {
|
||||
private fun exportEnumClass(klass: IrClass, superTypes: Iterable<IrType>): ExportedDeclaration? {
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> error(exportability.reason)
|
||||
is Exportability.NotNeeded -> return null
|
||||
@@ -241,7 +242,7 @@ class ExportModelGenerator(
|
||||
enumEntries
|
||||
.keysToMap(enumEntries::indexOf)
|
||||
|
||||
val (members, nestedClasses) = exportClassDeclarations(klass) { candidate ->
|
||||
val (members, nestedClasses) = exportClassDeclarations(klass, superTypes) { candidate ->
|
||||
val enumExportedMember = exportAsEnumMember(candidate, enumEntriesToOrdinal)
|
||||
enumExportedMember
|
||||
}
|
||||
@@ -253,15 +254,15 @@ class ExportModelGenerator(
|
||||
|
||||
return exportClass(
|
||||
klass,
|
||||
superTypes,
|
||||
listOf(privateConstructor) + members,
|
||||
nestedClasses
|
||||
).let {
|
||||
(it as ExportedRegularClass).copy(isAbstract = true)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportClassDeclarations(
|
||||
klass: IrClass,
|
||||
superTypes: Iterable<IrType>,
|
||||
specialProcessing: (IrDeclarationWithName) -> ExportedDeclaration? = { null }
|
||||
): ExportedClassDeclarationsInfo {
|
||||
val members = mutableListOf<ExportedDeclaration>()
|
||||
@@ -310,8 +311,8 @@ class ExportModelGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
if (klass.shouldContainImplementationOfMagicProperty()) {
|
||||
members.addMagicPropertyForInterfaceImplementation(klass)
|
||||
if (klass.shouldContainImplementationOfMagicProperty(superTypes)) {
|
||||
members.addMagicPropertyForInterfaceImplementation(klass, superTypes)
|
||||
} else if (klass.shouldNotBeImplemented()) {
|
||||
members.addMagicInterfaceProperty(klass)
|
||||
}
|
||||
@@ -326,7 +327,7 @@ class ExportModelGenerator(
|
||||
return isInterface && !isExternal
|
||||
}
|
||||
|
||||
private fun IrClass.shouldContainImplementationOfMagicProperty(): Boolean {
|
||||
private fun IrClass.shouldContainImplementationOfMagicProperty(superTypes: Iterable<IrType>): Boolean {
|
||||
return !isExternal && superTypes.any {
|
||||
val superClass = it.classOrNull?.owner ?: return@any false
|
||||
superClass.isInterface && superClass.isExported(context)
|
||||
@@ -337,8 +338,8 @@ class ExportModelGenerator(
|
||||
add(ExportedProperty(name = magicPropertyName, type = klass.generateTagType(), mutable = false, isMember = true, isField = true))
|
||||
}
|
||||
|
||||
private fun MutableList<ExportedDeclaration>.addMagicPropertyForInterfaceImplementation(klass: IrClass) {
|
||||
val superTypesToInheritanceMagicProperty = klass.superTypes.filter { it.shouldAddMagicPropertyOfSuper() }
|
||||
private fun MutableList<ExportedDeclaration>.addMagicPropertyForInterfaceImplementation(klass: IrClass, superTypes: Iterable<IrType>) {
|
||||
val superTypesToInheritanceMagicProperty = superTypes.filter { it.shouldAddMagicPropertyOfSuper() }
|
||||
|
||||
if (superTypesToInheritanceMagicProperty.isEmpty()) return
|
||||
|
||||
@@ -375,20 +376,20 @@ class ExportModelGenerator(
|
||||
|
||||
private fun exportClass(
|
||||
klass: IrClass,
|
||||
superTypes: Iterable<IrType>,
|
||||
members: List<ExportedDeclaration>,
|
||||
nestedClasses: List<ExportedClass>
|
||||
nestedClasses: List<ExportedClass>,
|
||||
): ExportedDeclaration {
|
||||
val typeParameters = klass.typeParameters.map(::exportTypeParameter)
|
||||
|
||||
// TODO: Handle non-exported super types
|
||||
val superClasses = superTypes
|
||||
.filter { !it.classifierOrFail.isInterface && it.canBeUsedAsSuperTypeOfExportedClasses() }
|
||||
.map { exportType(it, false) }
|
||||
.filter { it !is ExportedType.ErrorType }
|
||||
|
||||
val superType = klass.superTypes
|
||||
.firstOrNull { !it.classifierOrFail.isInterface && it.canBeUsedAsSuperTypeOfExportedClasses() }
|
||||
?.let { exportType(it).takeIf { it !is ExportedType.ErrorType } }
|
||||
|
||||
val superInterfaces = klass.superTypes
|
||||
val superInterfaces = superTypes
|
||||
.filter { it.classifierOrFail.isInterface }
|
||||
.map { exportType(it) }
|
||||
.map { exportType(it, false) }
|
||||
.filter { it !is ExportedType.ErrorType }
|
||||
|
||||
val name = klass.getExportedIdentifier()
|
||||
@@ -398,7 +399,7 @@ class ExportModelGenerator(
|
||||
ir = klass,
|
||||
name = name,
|
||||
members = members,
|
||||
superClass = superType,
|
||||
superClasses = superClasses,
|
||||
nestedClasses = nestedClasses,
|
||||
superInterfaces = superInterfaces,
|
||||
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!
|
||||
@@ -407,8 +408,8 @@ class ExportModelGenerator(
|
||||
ExportedRegularClass(
|
||||
name = name,
|
||||
isInterface = klass.isInterface,
|
||||
isAbstract = klass.modality == Modality.ABSTRACT || klass.modality == Modality.SEALED,
|
||||
superClass = superType,
|
||||
isAbstract = klass.modality == Modality.ABSTRACT || klass.modality == Modality.SEALED || klass.isEnumClass,
|
||||
superClasses = superClasses,
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = typeParameters,
|
||||
members = members,
|
||||
@@ -418,6 +419,9 @@ class ExportModelGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleType.collectSuperTransitiveHierarchy() =
|
||||
transitiveExportCollector.collectSuperTypesTransitiveHierarchyFor(this)
|
||||
|
||||
private fun exportAsEnumMember(
|
||||
candidate: IrDeclarationWithName,
|
||||
enumEntriesToOrdinal: Map<IrEnumEntry, Int>
|
||||
@@ -471,8 +475,15 @@ class ExportModelGenerator(
|
||||
private fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter {
|
||||
val constraint = typeParameter.superTypes.asSequence()
|
||||
.filter { it != context.irBuiltIns.anyNType }
|
||||
.map(::exportType)
|
||||
.filter { it !is ExportedType.ErrorType && it !is ExportedType.ImplicitlyExportedType }
|
||||
.map {
|
||||
val exportedType = exportType(it)
|
||||
if (exportedType is ExportedType.ImplicitlyExportedType && exportedType.exportedSupertype == ExportedType.Primitive.Any) {
|
||||
exportedType.copy(exportedSupertype = ExportedType.Primitive.Unknown)
|
||||
} else {
|
||||
exportedType
|
||||
}
|
||||
}
|
||||
.filter { it !is ExportedType.ErrorType }
|
||||
.toList()
|
||||
|
||||
return ExportedType.TypeParameter(
|
||||
@@ -487,7 +498,7 @@ class ExportModelGenerator(
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportType(type: IrType): ExportedType {
|
||||
private fun exportType(type: IrType, shouldCalculateExportedSupertypeForImplicit: Boolean = true): ExportedType {
|
||||
if (type is IrDynamicType)
|
||||
return ExportedType.Primitive.Any
|
||||
|
||||
@@ -535,6 +546,12 @@ class ExportModelGenerator(
|
||||
val isNonExportedExternal = klass.isExternal && !isExported
|
||||
val name = klass.getFqNameWithJsNameWhenAvailable(!isNonExportedExternal && generateNamespacesForPackages).asString()
|
||||
|
||||
val exportedSupertype = runIf(shouldCalculateExportedSupertypeForImplicit && isImplicitlyExported) {
|
||||
val transitiveExportedType = nonNullType.collectSuperTransitiveHierarchy()
|
||||
if (transitiveExportedType.isEmpty()) return@runIf null
|
||||
transitiveExportedType.map(::exportType).reduce(ExportedType::IntersectionType)
|
||||
} ?: ExportedType.Primitive.Any
|
||||
|
||||
when (klass.kind) {
|
||||
ClassKind.ANNOTATION_CLASS,
|
||||
ClassKind.ENUM_ENTRY ->
|
||||
@@ -550,7 +567,7 @@ class ExportModelGenerator(
|
||||
type.arguments.map { exportTypeArgument(it) },
|
||||
klass
|
||||
)
|
||||
}.withImplicitlyExported(isImplicitlyExported)
|
||||
}.withImplicitlyExported(isImplicitlyExported, exportedSupertype)
|
||||
}
|
||||
|
||||
else -> error("Unexpected classifier $classifier")
|
||||
@@ -683,7 +700,11 @@ private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, cont
|
||||
}
|
||||
|
||||
fun IrOverridableDeclaration<*>.isAllowedFakeOverriddenDeclaration(context: JsIrBackendContext): Boolean {
|
||||
if (this.resolveFakeOverride(allowAbstract = true)?.parentClassOrNull.isExportedInterface(context)) {
|
||||
val firstExportedRealOverride = runIf(isFakeOverride) {
|
||||
resolveFakeOverrideOrNull(allowAbstract = true) { it === this || it.parentClassOrNull?.isExported(context) != true }
|
||||
}
|
||||
|
||||
if (firstExportedRealOverride?.parentClassOrNull.isExportedInterface(context)) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+25
-14
@@ -14,6 +14,9 @@ import org.jetbrains.kotlin.ir.util.isObject
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import javax.lang.model.type.IntersectionType
|
||||
|
||||
private const val Nullable = "Nullable"
|
||||
private const val objects = "_objects_"
|
||||
@@ -176,10 +179,7 @@ class ExportModelToTsDeclarations {
|
||||
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members)
|
||||
|
||||
if (superClass != null)
|
||||
t = ExportedType.IntersectionType(t, superClass)
|
||||
|
||||
for (superInterface in superInterfaces) {
|
||||
for (superInterface in superClasses + superInterfaces) {
|
||||
t = ExportedType.IntersectionType(t, superInterface)
|
||||
}
|
||||
|
||||
@@ -210,13 +210,13 @@ class ExportModelToTsDeclarations {
|
||||
property.generateTypeScriptString(indent, prefix)
|
||||
} else {
|
||||
val propertyRef = "$objects.$propertyName"
|
||||
val shouldCreateExtraProperty = members.isNotEmpty() || superInterfaces.isNotEmpty() || superClass != null
|
||||
val shouldCreateExtraProperty = members.isNotEmpty() || superInterfaces.isNotEmpty() || superClasses.isNotEmpty()
|
||||
val newSuperClass = ExportedType.ClassType(propertyRef, emptyList(), ir).takeIf { shouldCreateExtraProperty }
|
||||
ExportedRegularClass(
|
||||
name = name,
|
||||
isInterface = false,
|
||||
isAbstract = true,
|
||||
superClass = newSuperClass,
|
||||
superClasses = listOfNotNull(newSuperClass),
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = emptyList(),
|
||||
members = listOf(ExportedConstructor(emptyList(), ExportedVisibility.PRIVATE)),
|
||||
@@ -232,7 +232,7 @@ class ExportModelToTsDeclarations {
|
||||
val keyword = if (isInterface) "interface" else "class"
|
||||
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
|
||||
|
||||
val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
|
||||
val superClassClause = superClasses.toExtendsClause(indent)
|
||||
val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
|
||||
|
||||
val (memberObjects, nestedDeclarations) = nestedClasses.partition { it.couldBeProperty() }
|
||||
@@ -279,12 +279,18 @@ class ExportModelToTsDeclarations {
|
||||
return if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
||||
}
|
||||
|
||||
private fun ExportedType.toExtendsClause(indent: String): String {
|
||||
val isImplicitlyExportedType = this is ExportedType.ImplicitlyExportedType
|
||||
val extendsClause = " extends ${toTypeScript(indent, isImplicitlyExportedType)}"
|
||||
return when {
|
||||
isImplicitlyExportedType -> " /*$extendsClause */"
|
||||
else -> extendsClause
|
||||
private fun List<ExportedType>.toExtendsClause(indent: String): String {
|
||||
if (isEmpty()) return ""
|
||||
|
||||
val implicitlyExportedClasses = filterIsInstance<ExportedType.ImplicitlyExportedType>()
|
||||
val implicitlyExportedClassesString = implicitlyExportedClasses.joinToString(", ") { it.toTypeScript(indent, true) }
|
||||
|
||||
return if (implicitlyExportedClasses.count() == count()) {
|
||||
" /* extends $implicitlyExportedClassesString */"
|
||||
} else {
|
||||
val originallyDefinedSuperClass = implicitlyExportedClassesString.takeIf { it.isNotEmpty() }?.let { "/* $it */ " }.orEmpty()
|
||||
val transitivelyDefinedSuperClass = firstIsInstance<ExportedType.ClassType>().toTypeScript(indent, false)
|
||||
" extends $originallyDefinedSuperClass$transitivelyDefinedSuperClass"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +389,12 @@ class ExportModelToTsDeclarations {
|
||||
is ExportedType.LiteralType.NumberLiteralType -> value.toString()
|
||||
is ExportedType.ImplicitlyExportedType -> {
|
||||
val typeString = type.toTypeScript("", true)
|
||||
if (isInCommentContext) typeString else ExportedType.Primitive.Any.toTypeScript(indent) + "/* $typeString */"
|
||||
if (isInCommentContext) {
|
||||
typeString
|
||||
} else {
|
||||
val superTypeString = exportedSupertype.toTypeScript(indent)
|
||||
superTypeString.let { if (exportedSupertype is ExportedType.IntersectionType) "($it)" else it } + "/* $typeString */"
|
||||
}
|
||||
}
|
||||
|
||||
is ExportedType.PropertyType -> "${container.toTypeScript(indent, isInCommentContext)}[${
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.isStdLibClass
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
private typealias SubstitutionMap = Map<IrTypeParameterSymbol, IrType>
|
||||
|
||||
class TransitiveExportCollector(val context: JsIrBackendContext) {
|
||||
private val typesCaches = hashMapOf<ClassWithAppliedArguments, Set<IrType>>()
|
||||
|
||||
fun collectSuperTypesTransitiveHierarchyFor(type: IrSimpleType): Set<IrType> {
|
||||
val classSymbol = type.classOrNull ?: return emptySet()
|
||||
|
||||
return typesCaches.getOrPut(ClassWithAppliedArguments(classSymbol, type.arguments)) {
|
||||
type.collectSuperTypesTransitiveHierarchy(type.calculateTypeSubstitutionMap(emptyMap()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleType.collectSuperTypesTransitiveHierarchy(typeSubstitutionMap: SubstitutionMap): Set<IrType> {
|
||||
val classifier = classOrNull ?: return emptySet()
|
||||
|
||||
return classifier.superTypes()
|
||||
.flatMap { (it as? IrSimpleType)?.collectTransitiveHierarchy(typeSubstitutionMap) ?: emptySet() }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun IrSimpleType.collectTransitiveHierarchy(typeSubstitutionMap: SubstitutionMap): Set<IrType> {
|
||||
val owner = classifier.owner as? IrClass ?: return emptySet()
|
||||
val substitutionMap = calculateTypeSubstitutionMap(typeSubstitutionMap)
|
||||
|
||||
return when {
|
||||
isBuiltInClass(owner) || isStdLibClass(owner) -> emptySet()
|
||||
owner.isExported(context) -> setOf(substitute(typeSubstitutionMap))
|
||||
else -> collectSuperTypesTransitiveHierarchy(substitutionMap)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrTypeArgument.getSubstitution(typeSubstitutionMap: SubstitutionMap): IrType {
|
||||
return when (this) {
|
||||
is IrType -> substitute(typeSubstitutionMap)
|
||||
is IrTypeProjection -> type.substitute(typeSubstitutionMap)
|
||||
else -> error("Unexpected ir type argument")
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleType.calculateTypeSubstitutionMap(typeSubstitutionMap: SubstitutionMap): SubstitutionMap {
|
||||
val classifier = classOrNull ?: error("Unexpected classifier $classifier for collecting transitive hierarchy")
|
||||
|
||||
return typeSubstitutionMap + classifier.owner.typeParameters.zip(arguments).associate {
|
||||
it.first.symbol to it.second.getSubstitution(typeSubstitutionMap)
|
||||
}
|
||||
}
|
||||
|
||||
private data class ClassWithAppliedArguments(val classSymbol: IrClassSymbol, val appliedArguments: List<IrTypeArgument>)
|
||||
}
|
||||
+4
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isChildOf
|
||||
|
||||
private val BODILESS_BUILTIN_CLASSES = listOf(
|
||||
"kotlin.String",
|
||||
@@ -44,6 +45,9 @@ private val BODILESS_BUILTIN_CLASSES = listOf(
|
||||
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
||||
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
|
||||
|
||||
fun isStdLibClass(declaration: IrDeclaration): Boolean =
|
||||
declaration is IrClass && declaration.fqNameWhenAvailable?.isChildOf(JsPackage) != false
|
||||
|
||||
private val JsPackage = FqName("kotlin.js")
|
||||
|
||||
private val JsIntrinsicFqName = FqName("kotlin.js.JsIntrinsic")
|
||||
|
||||
@@ -93,16 +93,24 @@ fun Collection<IrOverridableMember>.collectAndFilterRealOverrides(): Set<IrOverr
|
||||
else -> error("all members should be of the same kind, got ${map { it.render() }}")
|
||||
}
|
||||
|
||||
// TODO: use this implementation instead of any other
|
||||
fun <S : IrSymbol, T : IrOverridableDeclaration<S>> T.resolveFakeOverride(
|
||||
allowAbstract: Boolean = false,
|
||||
toSkip: (T) -> Boolean = { false }
|
||||
): T? =
|
||||
resolveFakeOverrideOrNull(allowAbstract, toSkip).also {
|
||||
if (allowAbstract && it == null) {
|
||||
error("No real overrides for ${this.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use this implementation instead of any other
|
||||
fun <S : IrSymbol, T : IrOverridableDeclaration<S>> T.resolveFakeOverrideOrNull(
|
||||
allowAbstract: Boolean = false,
|
||||
toSkip: (T) -> Boolean = { false }
|
||||
): T? {
|
||||
if (!isFakeOverride && !toSkip(this)) return this
|
||||
return if (allowAbstract) {
|
||||
val reals = collectRealOverrides(toSkip)
|
||||
if (reals.isEmpty()) error("No real overrides for ${this.render()}")
|
||||
reals.first()
|
||||
collectRealOverrides(toSkip).firstOrNull()
|
||||
} else {
|
||||
collectRealOverrides(toSkip, { it.modality == Modality.ABSTRACT })
|
||||
.let { realOverrides ->
|
||||
|
||||
@@ -11,8 +11,8 @@ declare namespace JS_TESTS {
|
||||
function generic1<T>(x: T): T;
|
||||
function generic2<T>(x: Nullable<T>): boolean;
|
||||
function genericWithConstraint<T extends string>(x: T): T;
|
||||
function genericWithMultipleConstraints<T extends RegExpMatchArray & Error>(x: T): T;
|
||||
function genericWithMultipleConstraints<T extends unknown/* kotlin.Comparable<T> */ & RegExpMatchArray & Error>(x: T): T;
|
||||
function generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
|
||||
function inlineFun(x: number, callback: (p0: number) => void): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ declare namespace JS_TESTS {
|
||||
constructor(value: any/* foo.NonExportedType */);
|
||||
get value(): any/* foo.NonExportedType */;
|
||||
set value(value: any/* foo.NonExportedType */);
|
||||
increment<T>(t: T): any/* foo.NonExportedType */;
|
||||
increment<T extends unknown/* foo.NonExportedType */>(t: T): any/* foo.NonExportedType */;
|
||||
}
|
||||
class B /* extends foo.NonExportedType */ {
|
||||
constructor(v: number);
|
||||
|
||||
@@ -13,6 +13,7 @@ declare namespace JS_TESTS {
|
||||
}
|
||||
}
|
||||
namespace foo {
|
||||
const fifth: (foo.Third<boolean> & foo.IA & foo.IG<boolean>)/* foo.Fifth<boolean> */;
|
||||
abstract class AC implements foo.I2 {
|
||||
constructor();
|
||||
get x(): string;
|
||||
@@ -100,5 +101,31 @@ declare namespace JS_TESTS {
|
||||
abstract get baz(): string;
|
||||
readonly __doNotUseOrImplementIt: foo.I3["__doNotUseOrImplementIt"];
|
||||
}
|
||||
interface IA {
|
||||
readonly foo: any;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "foo.IA": unique symbol;
|
||||
};
|
||||
}
|
||||
interface IG<T> {
|
||||
process(value: T): void;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "foo.IG": unique symbol;
|
||||
};
|
||||
}
|
||||
class Third<T> extends /* foo.Second */ foo.First {
|
||||
constructor();
|
||||
}
|
||||
class Sixth extends /* foo.Fifth<number> */ foo.Third<number> implements foo.IA, foo.IG<number>/*, foo.IC */ {
|
||||
constructor();
|
||||
process(value: number): void;
|
||||
get foo(): number;
|
||||
readonly __doNotUseOrImplementIt: foo.IA["__doNotUseOrImplementIt"] & foo.IG<number>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
class First {
|
||||
constructor();
|
||||
}
|
||||
function acceptForthLike<T extends (foo.Third<string> & foo.IA)/* foo.Forth<string> */>(forth: T): void;
|
||||
function acceptMoreGenericForthLike<T extends foo.IA/* foo.IB */ & foo.IA/* foo.IC */ & foo.First/* foo.Second */>(forth: T): void;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,3 +163,53 @@ enum class EC : I3 {
|
||||
|
||||
override fun bay(): String = "bay"
|
||||
}
|
||||
|
||||
// Save hierarhy
|
||||
|
||||
@JsExport
|
||||
interface IA {
|
||||
val foo: Any
|
||||
}
|
||||
|
||||
@JsExport
|
||||
interface IG<T> {
|
||||
fun process(value: T): Unit
|
||||
}
|
||||
|
||||
interface IB : IA
|
||||
|
||||
interface IC : IB {
|
||||
override val foo: Any
|
||||
}
|
||||
|
||||
interface ID : IC {
|
||||
override val foo: Int
|
||||
}
|
||||
|
||||
@JsExport
|
||||
open class Third<T>: Second()
|
||||
|
||||
open class Forth<A>: Third<A>(), IB, ID {
|
||||
override val foo: Int = 42
|
||||
}
|
||||
|
||||
open class Fifth<B>: Forth<B>(), IG<B> {
|
||||
override fun process(value: B) {}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class Sixth: Fifth<Int>(), IC
|
||||
|
||||
@JsExport
|
||||
open class First
|
||||
|
||||
open class Second: First()
|
||||
|
||||
@JsExport
|
||||
fun <T : Forth<String>> acceptForthLike(forth: T) {}
|
||||
|
||||
@JsExport
|
||||
fun <T> acceptMoreGenericForthLike(forth: T) where T: IB, T: IC, T: Second {}
|
||||
|
||||
@JsExport
|
||||
val fifth = Fifth<Boolean>()
|
||||
|
||||
@@ -13,7 +13,7 @@ declare namespace JS_TESTS {
|
||||
generic1<T>(x: T): T;
|
||||
generic2<T>(x: Nullable<T>): boolean;
|
||||
genericWithConstraint<T extends string>(x: T): T;
|
||||
genericWithMultipleConstraints<T extends RegExpMatchArray & Error>(x: T): T;
|
||||
genericWithMultipleConstraints<T extends unknown/* kotlin.Comparable<T> */ & RegExpMatchArray & Error>(x: T): T;
|
||||
generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
|
||||
inlineFun(x: number, callback: (p0: number) => void): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user