New IC: Add constants-in-companion-objects impact computation
When computing impacted symbols of changed symbols, previously we considered only the supertypes-inheritors type of impact, which is the most common type. This commit adds the constants-in-companion-objects type of impact to address KT-53266. We've also cleaned up impact computation to make it easier to add new types of impact in the future. ^KT-53266 In progress
This commit is contained in:
committed by
Alexander Likhachev
parent
3f0a93d6dd
commit
2ad047340f
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
@@ -203,9 +202,8 @@ class ChangesCollector {
|
||||
|
||||
private fun ClassProtoData.collectAllFromClass(isRemoved: Boolean, isAdded: Boolean, collectAllMembersForNewClass: Boolean = false) {
|
||||
val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName()
|
||||
val kind = Flags.CLASS_KIND.get(proto.flags)
|
||||
|
||||
if (kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
|
||||
if (proto.isCompanionObject) {
|
||||
val memberNames = getNonPrivateMemberNames()
|
||||
|
||||
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
||||
|
||||
@@ -455,27 +455,27 @@ open class IncrementalJvmCache(
|
||||
val allConstants = oldMap.keys + newMap.keys
|
||||
if (allConstants.isEmpty()) return
|
||||
|
||||
// If a constant is defined in a companion object, it will be found in the constantsMap of the containing class, not the
|
||||
// companion object's class, so we will need to correct its scope.
|
||||
// (See https://youtrack.jetbrains.com/issue/KT-44741#focus=Comments-27-5659564.0-0 for more details.)
|
||||
// Note: This only applies to a *constant* defined in a *companion object* (it's not an issue for inline functions, or top-level
|
||||
// constants, or constants in non-companion objects).
|
||||
val companionObjectClassId = if (kotlinClassInfo.classKind == KotlinClassHeader.Kind.CLASS) {
|
||||
val protoData = kotlinClassInfo.protoData as ClassProtoData
|
||||
if (protoData.proto.hasCompanionObjectName()) {
|
||||
val companionObjectName = Name.identifier(protoData.nameResolver.getString(protoData.proto.companionObjectName))
|
||||
kotlinClassInfo.classId.createNestedClassId(companionObjectName)
|
||||
} else null
|
||||
} else null
|
||||
val scope = companionObjectClassId?.asSingleFqName() ?: kotlinClassInfo.scopeFqName()
|
||||
|
||||
// Here we assume that the old and new classes have the same KotlinClassHeader.Kind, so that the scopes of the old and new
|
||||
// constants are the same and their values can be compared.
|
||||
// If the class kinds are different, the changes will be detected when comparing protos (in that case, the changes collected
|
||||
// here will be a subset of those changes).
|
||||
val scope = kotlinClassInfo.scopeFqName()
|
||||
for (const in allConstants) {
|
||||
changesCollector.collectMemberIfValueWasChanged(scope, const, oldMap[const], newMap[const])
|
||||
}
|
||||
|
||||
// If a constant is defined in a companion object of class A, its name and type will be found in the Kotlin metadata of
|
||||
// `A$Companion.class`, but its value will only be found in the Java bytecode code of `A.class` (see
|
||||
// `org.jetbrains.kotlin.incremental.classpathDiff.ConstantsInCompanionObjectImpact` for more details).
|
||||
// Therefore, if the value of `CONSTANT` in `A.class` has changed, we will report that `A.CONSTANT` has changed in the code
|
||||
// above, and report that `A.Companion.CONSTANT` is impacted in the code below.
|
||||
kotlinClassInfo.companionObject?.let { companionObjectClassId ->
|
||||
// Note that `companionObjectClassId` is the companion object of the current class. Here we assume that the previous class
|
||||
// also has a companion object with the same name. If that is not the case, that change will be detected when comparing
|
||||
// protos, and the report below will be imprecise/redundant, but it's okay to over-approximate the result.
|
||||
val companionObjectFqName = companionObjectClassId.asSingleFqName()
|
||||
for (const in allConstants) {
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
scope = companionObjectFqName, name = const, oldMap[const], newMap[const]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@@ -688,7 +688,7 @@ class KotlinClassInfo constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [ProtoData] of this class.
|
||||
* The [ProtoData] of this class.
|
||||
*
|
||||
* NOTE: The caller needs to ensure `classKind != KotlinClassHeader.Kind.MULTIFILE_CLASS` first, as the compiler doesn't write proto
|
||||
* data to [KotlinClassHeader.Kind.MULTIFILE_CLASS] classes.
|
||||
@@ -700,6 +700,25 @@ class KotlinClassInfo constructor(
|
||||
protoMapValue.toProtoData(classId.packageFqName)
|
||||
}
|
||||
|
||||
/** Name of the companion object of this class (default is "Companion") iff this class HAS a companion object, or null otherwise. */
|
||||
val companionObject: ClassId? by lazy {
|
||||
if (classKind == KotlinClassHeader.Kind.CLASS) {
|
||||
(protoData as ClassProtoData).getCompanionObjectName()?.let {
|
||||
classId.createNestedClassId(Name.identifier(it))
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
/** List of constants defined in this class iff this class IS a companion object, or null otherwise. The list could be empty. */
|
||||
val constantsInCompanionObject: List<String>? by lazy {
|
||||
if (classKind == KotlinClassHeader.Kind.CLASS) {
|
||||
val classProtoData = protoData as ClassProtoData
|
||||
if (classProtoData.proto.isCompanionObject) {
|
||||
classProtoData.getConstants()
|
||||
} else null
|
||||
} else null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createFrom(kotlinClass: LocalFileKotlinClass): KotlinClassInfo {
|
||||
|
||||
@@ -367,8 +367,23 @@ class DifferenceCalculatorForPackageFacade(
|
||||
private val ProtoBuf.Class.isSealed: Boolean
|
||||
get() = ProtoBuf.Modality.SEALED == Flags.MODALITY.get(flags)
|
||||
|
||||
internal val ProtoBuf.Class.isCompanionObject: Boolean
|
||||
get() = ProtoBuf.Class.Kind.COMPANION_OBJECT == Flags.CLASS_KIND.get(flags)
|
||||
|
||||
val ProtoBuf.Class.typeTableOrNull: ProtoBuf.TypeTable?
|
||||
get() = if (hasTypeTable()) typeTable else null
|
||||
|
||||
val ProtoBuf.Package.typeTableOrNull: ProtoBuf.TypeTable?
|
||||
get() = if (hasTypeTable()) typeTable else null
|
||||
|
||||
internal fun ClassProtoData.getCompanionObjectName(): String? {
|
||||
return if (proto.hasCompanionObjectName()) {
|
||||
nameResolver.getString(proto.companionObjectName)
|
||||
} else null
|
||||
}
|
||||
|
||||
internal fun ClassProtoData.getConstants(): List<String> {
|
||||
return proto.propertyList
|
||||
.filter { Flags.IS_CONST.get(it.flags) }
|
||||
.map { nameResolver.getString(it.name) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user