[K/JS] fix(TS): save type hierarchy for exported classes.
This commit is contained in:
Generated
-3
@@ -8,7 +8,4 @@
|
|||||||
<component name="KotlinCompilerSettings">
|
<component name="KotlinCompilerSettings">
|
||||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
|
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
|
||||||
<option name="version" value="1.8.0-dev-1006" />
|
|
||||||
</component>
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ sealed class ExportedClass : ExportedDeclaration() {
|
|||||||
abstract val name: String
|
abstract val name: String
|
||||||
abstract val ir: IrClass
|
abstract val ir: IrClass
|
||||||
abstract val members: List<ExportedDeclaration>
|
abstract val members: List<ExportedDeclaration>
|
||||||
abstract val superClass: ExportedType?
|
abstract val superClasses: List<ExportedType>
|
||||||
abstract val superInterfaces: List<ExportedType>
|
abstract val superInterfaces: List<ExportedType>
|
||||||
abstract val nestedClasses: List<ExportedClass>
|
abstract val nestedClasses: List<ExportedClass>
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ data class ExportedRegularClass(
|
|||||||
override val name: String,
|
override val name: String,
|
||||||
val isInterface: Boolean = false,
|
val isInterface: Boolean = false,
|
||||||
val isAbstract: Boolean = false,
|
val isAbstract: Boolean = false,
|
||||||
override val superClass: ExportedType? = null,
|
override val superClasses: List<ExportedType> = emptyList(),
|
||||||
override val superInterfaces: List<ExportedType> = emptyList(),
|
override val superInterfaces: List<ExportedType> = emptyList(),
|
||||||
val typeParameters: List<ExportedType.TypeParameter>,
|
val typeParameters: List<ExportedType.TypeParameter>,
|
||||||
override val members: List<ExportedDeclaration>,
|
override val members: List<ExportedDeclaration>,
|
||||||
@@ -89,7 +89,7 @@ data class ExportedRegularClass(
|
|||||||
|
|
||||||
data class ExportedObject(
|
data class ExportedObject(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val superClass: ExportedType? = null,
|
override val superClasses: List<ExportedType> = emptyList(),
|
||||||
override val superInterfaces: List<ExportedType> = emptyList(),
|
override val superInterfaces: List<ExportedType> = emptyList(),
|
||||||
override val members: List<ExportedDeclaration>,
|
override val members: List<ExportedDeclaration>,
|
||||||
override val nestedClasses: List<ExportedClass>,
|
override val nestedClasses: List<ExportedClass>,
|
||||||
@@ -115,6 +115,7 @@ sealed class ExportedType {
|
|||||||
object String : Primitive("string")
|
object String : Primitive("string")
|
||||||
object Throwable : Primitive("Error")
|
object Throwable : Primitive("Error")
|
||||||
object Any : Primitive("any")
|
object Any : Primitive("any")
|
||||||
|
object Unknown : Primitive("unknown")
|
||||||
object Unit : Primitive("void")
|
object Unit : Primitive("void")
|
||||||
object Nothing : Primitive("never")
|
object Nothing : Primitive("never")
|
||||||
object UniqueSymbol : Primitive("unique symbol")
|
object UniqueSymbol : Primitive("unique symbol")
|
||||||
@@ -147,16 +148,16 @@ sealed class ExportedType {
|
|||||||
|
|
||||||
class PropertyType(val container: ExportedType, val propertyName: ExportedType) : 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) =
|
override fun withNullability(nullable: Boolean) =
|
||||||
ImplicitlyExportedType(type.withNullability(nullable))
|
ImplicitlyExportedType(type.withNullability(nullable), exportedSupertype.withNullability(nullable))
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun withNullability(nullable: Boolean) =
|
open fun withNullability(nullable: Boolean) =
|
||||||
if (nullable) Nullable(this) else this
|
if (nullable) Nullable(this) else this
|
||||||
|
|
||||||
fun withImplicitlyExported(implicitlyExportedType: Boolean) =
|
fun withImplicitlyExported(implicitlyExportedType: Boolean, exportedSupertype: ExportedType) =
|
||||||
if (implicitlyExportedType) ImplicitlyExportedType(this) else this
|
if (implicitlyExportedType) ImplicitlyExportedType(this, exportedSupertype) else this
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ExportedVisibility(val keyword: String) {
|
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.ir.util.*
|
||||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import org.jetbrains.kotlin.utils.keysToMap
|
import org.jetbrains.kotlin.utils.keysToMap
|
||||||
|
|
||||||
private const val magicPropertyName = "__doNotUseOrImplementIt"
|
private const val magicPropertyName = "__doNotUseOrImplementIt"
|
||||||
|
|
||||||
class ExportModelGenerator(
|
class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespacesForPackages: Boolean) {
|
||||||
val context: JsIrBackendContext,
|
private val transitiveExportCollector = TransitiveExportCollector(context)
|
||||||
val generateNamespacesForPackages: Boolean
|
|
||||||
) {
|
|
||||||
fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
||||||
val namespaceFqName = file.fqName
|
val namespaceFqName = file.fqName
|
||||||
val exports = file.declarations.flatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
|
val exports = file.declarations.flatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
|
||||||
@@ -64,10 +64,14 @@ class ExportModelGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportClass(candidate: IrClass) = if (candidate.isEnumClass) {
|
private fun exportClass(candidate: IrClass): ExportedDeclaration? {
|
||||||
exportEnumClass(candidate)
|
val superTypes = candidate.defaultType.collectSuperTransitiveHierarchy() + candidate.superTypes
|
||||||
} else {
|
|
||||||
exportOrdinaryClass(candidate)
|
return if (candidate.isEnumClass) {
|
||||||
|
exportEnumClass(candidate, superTypes)
|
||||||
|
} else {
|
||||||
|
exportOrdinaryClass(candidate, superTypes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? {
|
private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? {
|
||||||
@@ -203,9 +207,7 @@ class ExportModelGenerator(
|
|||||||
return Exportability.Allowed
|
return Exportability.Allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportOrdinaryClass(
|
private fun exportOrdinaryClass(klass: IrClass, superTypes: Iterable<IrType>): ExportedDeclaration? {
|
||||||
klass: IrClass
|
|
||||||
): ExportedDeclaration? {
|
|
||||||
when (val exportability = classExportability(klass)) {
|
when (val exportability = classExportability(klass)) {
|
||||||
is Exportability.Prohibited -> error(exportability.reason)
|
is Exportability.Prohibited -> error(exportability.reason)
|
||||||
is Exportability.NotNeeded -> return null
|
is Exportability.NotNeeded -> return null
|
||||||
@@ -213,18 +215,17 @@ class ExportModelGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val (members, nestedClasses) = exportClassDeclarations(klass)
|
val (members, nestedClasses) = exportClassDeclarations(klass, superTypes)
|
||||||
|
|
||||||
return exportClass(
|
return exportClass(
|
||||||
klass,
|
klass,
|
||||||
|
superTypes,
|
||||||
members,
|
members,
|
||||||
nestedClasses
|
nestedClasses
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportEnumClass(
|
private fun exportEnumClass(klass: IrClass, superTypes: Iterable<IrType>): ExportedDeclaration? {
|
||||||
klass: IrClass
|
|
||||||
): ExportedDeclaration? {
|
|
||||||
when (val exportability = classExportability(klass)) {
|
when (val exportability = classExportability(klass)) {
|
||||||
is Exportability.Prohibited -> error(exportability.reason)
|
is Exportability.Prohibited -> error(exportability.reason)
|
||||||
is Exportability.NotNeeded -> return null
|
is Exportability.NotNeeded -> return null
|
||||||
@@ -241,7 +242,7 @@ class ExportModelGenerator(
|
|||||||
enumEntries
|
enumEntries
|
||||||
.keysToMap(enumEntries::indexOf)
|
.keysToMap(enumEntries::indexOf)
|
||||||
|
|
||||||
val (members, nestedClasses) = exportClassDeclarations(klass) { candidate ->
|
val (members, nestedClasses) = exportClassDeclarations(klass, superTypes) { candidate ->
|
||||||
val enumExportedMember = exportAsEnumMember(candidate, enumEntriesToOrdinal)
|
val enumExportedMember = exportAsEnumMember(candidate, enumEntriesToOrdinal)
|
||||||
enumExportedMember
|
enumExportedMember
|
||||||
}
|
}
|
||||||
@@ -253,15 +254,15 @@ class ExportModelGenerator(
|
|||||||
|
|
||||||
return exportClass(
|
return exportClass(
|
||||||
klass,
|
klass,
|
||||||
|
superTypes,
|
||||||
listOf(privateConstructor) + members,
|
listOf(privateConstructor) + members,
|
||||||
nestedClasses
|
nestedClasses
|
||||||
).let {
|
)
|
||||||
(it as ExportedRegularClass).copy(isAbstract = true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun exportClassDeclarations(
|
private fun exportClassDeclarations(
|
||||||
klass: IrClass,
|
klass: IrClass,
|
||||||
|
superTypes: Iterable<IrType>,
|
||||||
specialProcessing: (IrDeclarationWithName) -> ExportedDeclaration? = { null }
|
specialProcessing: (IrDeclarationWithName) -> ExportedDeclaration? = { null }
|
||||||
): ExportedClassDeclarationsInfo {
|
): ExportedClassDeclarationsInfo {
|
||||||
val members = mutableListOf<ExportedDeclaration>()
|
val members = mutableListOf<ExportedDeclaration>()
|
||||||
@@ -310,8 +311,8 @@ class ExportModelGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (klass.shouldContainImplementationOfMagicProperty()) {
|
if (klass.shouldContainImplementationOfMagicProperty(superTypes)) {
|
||||||
members.addMagicPropertyForInterfaceImplementation(klass)
|
members.addMagicPropertyForInterfaceImplementation(klass, superTypes)
|
||||||
} else if (klass.shouldNotBeImplemented()) {
|
} else if (klass.shouldNotBeImplemented()) {
|
||||||
members.addMagicInterfaceProperty(klass)
|
members.addMagicInterfaceProperty(klass)
|
||||||
}
|
}
|
||||||
@@ -326,7 +327,7 @@ class ExportModelGenerator(
|
|||||||
return isInterface && !isExternal
|
return isInterface && !isExternal
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrClass.shouldContainImplementationOfMagicProperty(): Boolean {
|
private fun IrClass.shouldContainImplementationOfMagicProperty(superTypes: Iterable<IrType>): Boolean {
|
||||||
return !isExternal && superTypes.any {
|
return !isExternal && superTypes.any {
|
||||||
val superClass = it.classOrNull?.owner ?: return@any false
|
val superClass = it.classOrNull?.owner ?: return@any false
|
||||||
superClass.isInterface && superClass.isExported(context)
|
superClass.isInterface && superClass.isExported(context)
|
||||||
@@ -337,8 +338,8 @@ class ExportModelGenerator(
|
|||||||
add(ExportedProperty(name = magicPropertyName, type = klass.generateTagType(), mutable = false, isMember = true, isField = true))
|
add(ExportedProperty(name = magicPropertyName, type = klass.generateTagType(), mutable = false, isMember = true, isField = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<ExportedDeclaration>.addMagicPropertyForInterfaceImplementation(klass: IrClass) {
|
private fun MutableList<ExportedDeclaration>.addMagicPropertyForInterfaceImplementation(klass: IrClass, superTypes: Iterable<IrType>) {
|
||||||
val superTypesToInheritanceMagicProperty = klass.superTypes.filter { it.shouldAddMagicPropertyOfSuper() }
|
val superTypesToInheritanceMagicProperty = superTypes.filter { it.shouldAddMagicPropertyOfSuper() }
|
||||||
|
|
||||||
if (superTypesToInheritanceMagicProperty.isEmpty()) return
|
if (superTypesToInheritanceMagicProperty.isEmpty()) return
|
||||||
|
|
||||||
@@ -375,20 +376,20 @@ class ExportModelGenerator(
|
|||||||
|
|
||||||
private fun exportClass(
|
private fun exportClass(
|
||||||
klass: IrClass,
|
klass: IrClass,
|
||||||
|
superTypes: Iterable<IrType>,
|
||||||
members: List<ExportedDeclaration>,
|
members: List<ExportedDeclaration>,
|
||||||
nestedClasses: List<ExportedClass>
|
nestedClasses: List<ExportedClass>,
|
||||||
): ExportedDeclaration {
|
): ExportedDeclaration {
|
||||||
val typeParameters = klass.typeParameters.map(::exportTypeParameter)
|
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
|
val superInterfaces = superTypes
|
||||||
.firstOrNull { !it.classifierOrFail.isInterface && it.canBeUsedAsSuperTypeOfExportedClasses() }
|
|
||||||
?.let { exportType(it).takeIf { it !is ExportedType.ErrorType } }
|
|
||||||
|
|
||||||
val superInterfaces = klass.superTypes
|
|
||||||
.filter { it.classifierOrFail.isInterface }
|
.filter { it.classifierOrFail.isInterface }
|
||||||
.map { exportType(it) }
|
.map { exportType(it, false) }
|
||||||
.filter { it !is ExportedType.ErrorType }
|
.filter { it !is ExportedType.ErrorType }
|
||||||
|
|
||||||
val name = klass.getExportedIdentifier()
|
val name = klass.getExportedIdentifier()
|
||||||
@@ -398,7 +399,7 @@ class ExportModelGenerator(
|
|||||||
ir = klass,
|
ir = klass,
|
||||||
name = name,
|
name = name,
|
||||||
members = members,
|
members = members,
|
||||||
superClass = superType,
|
superClasses = superClasses,
|
||||||
nestedClasses = nestedClasses,
|
nestedClasses = nestedClasses,
|
||||||
superInterfaces = superInterfaces,
|
superInterfaces = superInterfaces,
|
||||||
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!
|
irGetter = context.mapping.objectToGetInstanceFunction[klass]!!
|
||||||
@@ -407,8 +408,8 @@ class ExportModelGenerator(
|
|||||||
ExportedRegularClass(
|
ExportedRegularClass(
|
||||||
name = name,
|
name = name,
|
||||||
isInterface = klass.isInterface,
|
isInterface = klass.isInterface,
|
||||||
isAbstract = klass.modality == Modality.ABSTRACT || klass.modality == Modality.SEALED,
|
isAbstract = klass.modality == Modality.ABSTRACT || klass.modality == Modality.SEALED || klass.isEnumClass,
|
||||||
superClass = superType,
|
superClasses = superClasses,
|
||||||
superInterfaces = superInterfaces,
|
superInterfaces = superInterfaces,
|
||||||
typeParameters = typeParameters,
|
typeParameters = typeParameters,
|
||||||
members = members,
|
members = members,
|
||||||
@@ -418,6 +419,9 @@ class ExportModelGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IrSimpleType.collectSuperTransitiveHierarchy() =
|
||||||
|
transitiveExportCollector.collectSuperTypesTransitiveHierarchyFor(this)
|
||||||
|
|
||||||
private fun exportAsEnumMember(
|
private fun exportAsEnumMember(
|
||||||
candidate: IrDeclarationWithName,
|
candidate: IrDeclarationWithName,
|
||||||
enumEntriesToOrdinal: Map<IrEnumEntry, Int>
|
enumEntriesToOrdinal: Map<IrEnumEntry, Int>
|
||||||
@@ -471,8 +475,15 @@ class ExportModelGenerator(
|
|||||||
private fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter {
|
private fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter {
|
||||||
val constraint = typeParameter.superTypes.asSequence()
|
val constraint = typeParameter.superTypes.asSequence()
|
||||||
.filter { it != context.irBuiltIns.anyNType }
|
.filter { it != context.irBuiltIns.anyNType }
|
||||||
.map(::exportType)
|
.map {
|
||||||
.filter { it !is ExportedType.ErrorType && it !is ExportedType.ImplicitlyExportedType }
|
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()
|
.toList()
|
||||||
|
|
||||||
return ExportedType.TypeParameter(
|
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)
|
if (type is IrDynamicType)
|
||||||
return ExportedType.Primitive.Any
|
return ExportedType.Primitive.Any
|
||||||
|
|
||||||
@@ -535,6 +546,12 @@ class ExportModelGenerator(
|
|||||||
val isNonExportedExternal = klass.isExternal && !isExported
|
val isNonExportedExternal = klass.isExternal && !isExported
|
||||||
val name = klass.getFqNameWithJsNameWhenAvailable(!isNonExportedExternal && generateNamespacesForPackages).asString()
|
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) {
|
when (klass.kind) {
|
||||||
ClassKind.ANNOTATION_CLASS,
|
ClassKind.ANNOTATION_CLASS,
|
||||||
ClassKind.ENUM_ENTRY ->
|
ClassKind.ENUM_ENTRY ->
|
||||||
@@ -550,7 +567,7 @@ class ExportModelGenerator(
|
|||||||
type.arguments.map { exportTypeArgument(it) },
|
type.arguments.map { exportTypeArgument(it) },
|
||||||
klass
|
klass
|
||||||
)
|
)
|
||||||
}.withImplicitlyExported(isImplicitlyExported)
|
}.withImplicitlyExported(isImplicitlyExported, exportedSupertype)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> error("Unexpected classifier $classifier")
|
else -> error("Unexpected classifier $classifier")
|
||||||
@@ -683,7 +700,11 @@ private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun IrOverridableDeclaration<*>.isAllowedFakeOverriddenDeclaration(context: JsIrBackendContext): Boolean {
|
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
|
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.ir.util.parentAsClass
|
||||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
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 Nullable = "Nullable"
|
||||||
private const val objects = "_objects_"
|
private const val objects = "_objects_"
|
||||||
@@ -176,10 +179,7 @@ class ExportModelToTsDeclarations {
|
|||||||
|
|
||||||
var t: ExportedType = ExportedType.InlineInterfaceType(members)
|
var t: ExportedType = ExportedType.InlineInterfaceType(members)
|
||||||
|
|
||||||
if (superClass != null)
|
for (superInterface in superClasses + superInterfaces) {
|
||||||
t = ExportedType.IntersectionType(t, superClass)
|
|
||||||
|
|
||||||
for (superInterface in superInterfaces) {
|
|
||||||
t = ExportedType.IntersectionType(t, superInterface)
|
t = ExportedType.IntersectionType(t, superInterface)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,13 +210,13 @@ class ExportModelToTsDeclarations {
|
|||||||
property.generateTypeScriptString(indent, prefix)
|
property.generateTypeScriptString(indent, prefix)
|
||||||
} else {
|
} else {
|
||||||
val propertyRef = "$objects.$propertyName"
|
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 }
|
val newSuperClass = ExportedType.ClassType(propertyRef, emptyList(), ir).takeIf { shouldCreateExtraProperty }
|
||||||
ExportedRegularClass(
|
ExportedRegularClass(
|
||||||
name = name,
|
name = name,
|
||||||
isInterface = false,
|
isInterface = false,
|
||||||
isAbstract = true,
|
isAbstract = true,
|
||||||
superClass = newSuperClass,
|
superClasses = listOfNotNull(newSuperClass),
|
||||||
superInterfaces = superInterfaces,
|
superInterfaces = superInterfaces,
|
||||||
typeParameters = emptyList(),
|
typeParameters = emptyList(),
|
||||||
members = listOf(ExportedConstructor(emptyList(), ExportedVisibility.PRIVATE)),
|
members = listOf(ExportedConstructor(emptyList(), ExportedVisibility.PRIVATE)),
|
||||||
@@ -232,7 +232,7 @@ class ExportModelToTsDeclarations {
|
|||||||
val keyword = if (isInterface) "interface" else "class"
|
val keyword = if (isInterface) "interface" else "class"
|
||||||
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
|
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 superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
|
||||||
|
|
||||||
val (memberObjects, nestedDeclarations) = nestedClasses.partition { it.couldBeProperty() }
|
val (memberObjects, nestedDeclarations) = nestedClasses.partition { it.couldBeProperty() }
|
||||||
@@ -279,12 +279,18 @@ class ExportModelToTsDeclarations {
|
|||||||
return if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
return if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExportedType.toExtendsClause(indent: String): String {
|
private fun List<ExportedType>.toExtendsClause(indent: String): String {
|
||||||
val isImplicitlyExportedType = this is ExportedType.ImplicitlyExportedType
|
if (isEmpty()) return ""
|
||||||
val extendsClause = " extends ${toTypeScript(indent, isImplicitlyExportedType)}"
|
|
||||||
return when {
|
val implicitlyExportedClasses = filterIsInstance<ExportedType.ImplicitlyExportedType>()
|
||||||
isImplicitlyExportedType -> " /*$extendsClause */"
|
val implicitlyExportedClassesString = implicitlyExportedClasses.joinToString(", ") { it.toTypeScript(indent, true) }
|
||||||
else -> extendsClause
|
|
||||||
|
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.LiteralType.NumberLiteralType -> value.toString()
|
||||||
is ExportedType.ImplicitlyExportedType -> {
|
is ExportedType.ImplicitlyExportedType -> {
|
||||||
val typeString = type.toTypeScript("", true)
|
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)}[${
|
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.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.isChildOf
|
||||||
|
|
||||||
private val BODILESS_BUILTIN_CLASSES = listOf(
|
private val BODILESS_BUILTIN_CLASSES = listOf(
|
||||||
"kotlin.String",
|
"kotlin.String",
|
||||||
@@ -44,6 +45,9 @@ private val BODILESS_BUILTIN_CLASSES = listOf(
|
|||||||
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
||||||
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
|
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 JsPackage = FqName("kotlin.js")
|
||||||
|
|
||||||
private val JsIntrinsicFqName = FqName("kotlin.js.JsIntrinsic")
|
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() }}")
|
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(
|
fun <S : IrSymbol, T : IrOverridableDeclaration<S>> T.resolveFakeOverride(
|
||||||
allowAbstract: Boolean = false,
|
allowAbstract: Boolean = false,
|
||||||
toSkip: (T) -> 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? {
|
): T? {
|
||||||
if (!isFakeOverride && !toSkip(this)) return this
|
if (!isFakeOverride && !toSkip(this)) return this
|
||||||
return if (allowAbstract) {
|
return if (allowAbstract) {
|
||||||
val reals = collectRealOverrides(toSkip)
|
collectRealOverrides(toSkip).firstOrNull()
|
||||||
if (reals.isEmpty()) error("No real overrides for ${this.render()}")
|
|
||||||
reals.first()
|
|
||||||
} else {
|
} else {
|
||||||
collectRealOverrides(toSkip, { it.modality == Modality.ABSTRACT })
|
collectRealOverrides(toSkip, { it.modality == Modality.ABSTRACT })
|
||||||
.let { realOverrides ->
|
.let { realOverrides ->
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ declare namespace JS_TESTS {
|
|||||||
function generic1<T>(x: T): T;
|
function generic1<T>(x: T): T;
|
||||||
function generic2<T>(x: Nullable<T>): boolean;
|
function generic2<T>(x: Nullable<T>): boolean;
|
||||||
function genericWithConstraint<T extends string>(x: T): T;
|
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 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;
|
function inlineFun(x: number, callback: (p0: number) => void): void;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ declare namespace JS_TESTS {
|
|||||||
constructor(value: any/* foo.NonExportedType */);
|
constructor(value: any/* foo.NonExportedType */);
|
||||||
get value(): any/* foo.NonExportedType */;
|
get value(): any/* foo.NonExportedType */;
|
||||||
set value(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 */ {
|
class B /* extends foo.NonExportedType */ {
|
||||||
constructor(v: number);
|
constructor(v: number);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ declare namespace JS_TESTS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace foo {
|
namespace foo {
|
||||||
|
const fifth: (foo.Third<boolean> & foo.IA & foo.IG<boolean>)/* foo.Fifth<boolean> */;
|
||||||
abstract class AC implements foo.I2 {
|
abstract class AC implements foo.I2 {
|
||||||
constructor();
|
constructor();
|
||||||
get x(): string;
|
get x(): string;
|
||||||
@@ -100,5 +101,31 @@ declare namespace JS_TESTS {
|
|||||||
abstract get baz(): string;
|
abstract get baz(): string;
|
||||||
readonly __doNotUseOrImplementIt: foo.I3["__doNotUseOrImplementIt"];
|
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"
|
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;
|
generic1<T>(x: T): T;
|
||||||
generic2<T>(x: Nullable<T>): boolean;
|
generic2<T>(x: Nullable<T>): boolean;
|
||||||
genericWithConstraint<T extends string>(x: T): T;
|
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>;
|
generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
|
||||||
inlineFun(x: number, callback: (p0: number) => void): void;
|
inlineFun(x: number, callback: (p0: number) => void): void;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user