New IC: Include inline property accessors in class/package members
Problem: ClasspathChangesComputer (a core component of the new IC) uses
the existing inline function analysis from the old IC
(IncrementalJvmCache.InlineFunctionsMap). If an inline property accessor
has changed, the inline function analysis will report the name of the
property accessor, not the name of the property.
ClasspathChangesComputer doesn't see the property accessor's name in the
list of class/package members, so it will throw an exception.
Solution: There are 2 options:
1. If an inline property accessor has changed, the inline function
analysis can report the name of the property instead of the
property accessor.
2. ClasspathChangesComputer can include property accessors in the list
of class/package members.
In this commit, we will choose option 2 as it is simpler.
Test: New KotlinOnlyClasspathChangesComputerTest.testPropertyAccessors
Small cleanup - PLS SQUASH INTO PREVIOUS COMMIT
- Address review
- Fix failed tests
- Add some trivial changes
^KT-53871 Fixed
This commit is contained in:
committed by
nataliya.valtman
parent
2ba7c7b8a9
commit
03f83ff339
@@ -36,32 +36,6 @@ class ChangesCollector {
|
|||||||
fun protoDataChanges(): Map<FqName, ProtoData> = storage
|
fun protoDataChanges(): Map<FqName, ProtoData> = storage
|
||||||
fun protoDataRemoved(): List<FqName> = removed
|
fun protoDataRemoved(): List<FqName> = removed
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>) =
|
|
||||||
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
|
||||||
|
|
||||||
fun ClassProtoData.getNonPrivateMemberNames(): Set<String> {
|
|
||||||
return proto.getNonPrivateNames(
|
|
||||||
nameResolver,
|
|
||||||
// The types below should match the logic at `DifferenceCalculatorForClass.difference`
|
|
||||||
ProtoBuf.Class::getConstructorList,
|
|
||||||
ProtoBuf.Class::getFunctionList,
|
|
||||||
ProtoBuf.Class::getPropertyList,
|
|
||||||
ProtoBuf.Class::getTypeAliasList
|
|
||||||
) + proto.enumEntryList.map { nameResolver.getString(it.name) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun PackagePartProtoData.getNonPrivateMemberNames(): Set<String> {
|
|
||||||
return proto.getNonPrivateNames(
|
|
||||||
nameResolver,
|
|
||||||
// The types below should match the logic at `DifferenceCalculatorForPackageFacade.difference`
|
|
||||||
ProtoBuf.Package::getFunctionList,
|
|
||||||
ProtoBuf.Package::getPropertyList,
|
|
||||||
ProtoBuf.Package::getTypeAliasList
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun changes(): List<ChangeInfo> {
|
fun changes(): List<ChangeInfo> {
|
||||||
val changes = arrayListOf<ChangeInfo>()
|
val changes = arrayListOf<ChangeInfo>()
|
||||||
|
|
||||||
@@ -192,7 +166,7 @@ class ChangesCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
|
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
|
||||||
val memberNames = getNonPrivateMemberNames()
|
val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true)
|
||||||
if (isRemoved) {
|
if (isRemoved) {
|
||||||
collectRemovedMembers(packageFqName, memberNames)
|
collectRemovedMembers(packageFqName, memberNames)
|
||||||
} else {
|
} else {
|
||||||
@@ -204,14 +178,14 @@ class ChangesCollector {
|
|||||||
val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName()
|
val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName()
|
||||||
|
|
||||||
if (proto.isCompanionObject) {
|
if (proto.isCompanionObject) {
|
||||||
val memberNames = getNonPrivateMemberNames()
|
val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true)
|
||||||
|
|
||||||
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
||||||
collectMember(classFqName.parent(), classFqName.shortName().asString())
|
collectMember(classFqName.parent(), classFqName.shortName().asString())
|
||||||
memberNames.forEach { collectMember(classFqName, it) }
|
memberNames.forEach { collectMember(classFqName, it) }
|
||||||
} else {
|
} else {
|
||||||
if (!isRemoved && collectAllMembersForNewClass) {
|
if (!isRemoved && collectAllMembersForNewClass) {
|
||||||
val memberNames = getNonPrivateMemberNames()
|
val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true)
|
||||||
memberNames.forEach { this@ChangesCollector.collectChangedMember(classFqName, it) }
|
memberNames.forEach { this@ChangesCollector.collectChangedMember(classFqName, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ import com.intellij.util.io.EnumeratorStringDescriptor
|
|||||||
import gnu.trove.THashSet
|
import gnu.trove.THashSet
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||||
|
import org.jetbrains.kotlin.incremental.DifferenceCalculatorForClass.Companion.getNonPrivateMembers
|
||||||
|
import org.jetbrains.kotlin.incremental.DifferenceCalculatorForPackageFacade.Companion.getNonPrivateMembers
|
||||||
import org.jetbrains.kotlin.incremental.storage.*
|
import org.jetbrains.kotlin.incremental.storage.*
|
||||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
import org.jetbrains.kotlin.inline.inlineAccessors
|
||||||
|
import org.jetbrains.kotlin.inline.inlineFunctionsAndAccessors
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
||||||
@@ -39,7 +42,8 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader.*
|
import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_CODE
|
||||||
|
import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
|
||||||
@@ -570,19 +574,26 @@ open class IncrementalJvmCache(
|
|||||||
val key = kotlinClassInfo.className.internalName
|
val key = kotlinClassInfo.className.internalName
|
||||||
val oldMap = storage[key] ?: emptyMap()
|
val oldMap = storage[key] ?: emptyMap()
|
||||||
|
|
||||||
val newMap = kotlinClassInfo.inlineFunctionsMap
|
val newMap = kotlinClassInfo.inlineFunctionsAndAccessorsMap
|
||||||
if (newMap.isNotEmpty()) {
|
if (newMap.isNotEmpty()) {
|
||||||
storage[key] = newMap
|
storage[key] = newMap
|
||||||
} else {
|
} else {
|
||||||
storage.remove(key)
|
storage.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (fn in oldMap.keys + newMap.keys) {
|
for (methodSignature in oldMap.keys + newMap.keys) {
|
||||||
|
val inlineFunctionOrAccessorName = functionNameBySignature(methodSignature)
|
||||||
|
// Note: If we detect a change in an inline property accessor (e.g., `getFoo`), we have two options:
|
||||||
|
// 1. Report that property `foo` has changed (and ignore `getFoo`)
|
||||||
|
// 2. Report that property accessor `getFoo` has changed (and ignore `foo`)
|
||||||
|
// Both options are possible (the compiler generates `LookupSymbol`s for both `foo` and `getFoo` when `getFoo` is inlined).
|
||||||
|
// Currently, we choose option 2 as it is simpler (i.e., even though inline property accessors are not immediate members of
|
||||||
|
// a class/package, we treat them like immediate members).
|
||||||
changesCollector.collectMemberIfValueWasChanged(
|
changesCollector.collectMemberIfValueWasChanged(
|
||||||
kotlinClassInfo.scopeFqName(),
|
kotlinClassInfo.scopeFqName(),
|
||||||
functionNameBySignature(fn),
|
inlineFunctionOrAccessorName,
|
||||||
oldMap[fn],
|
oldMap[methodSignature],
|
||||||
newMap[fn]
|
newMap[methodSignature]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -659,6 +670,28 @@ fun <K : Comparable<K>, V> Map<K, V>.dumpMap(dumpValue: (V) -> String): String =
|
|||||||
fun <T : Comparable<T>> Collection<T>.dumpCollection(): String =
|
fun <T : Comparable<T>> Collection<T>.dumpCollection(): String =
|
||||||
"[${sorted().joinToString(", ", transform = Any::toString)}]"
|
"[${sorted().joinToString(", ", transform = Any::toString)}]"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param includeInlineAccessors If `true`, also include inline property accessors in the returned list. This is necessary because property
|
||||||
|
* accessors are not immediate members of a class/package, and in some cases we treat them like immediate members (see
|
||||||
|
* [org.jetbrains.kotlin.incremental.IncrementalJvmCache.InlineFunctionsMap.process]). Note that here we deal with *inline* property
|
||||||
|
* accessors only as [org.jetbrains.kotlin.incremental.IncrementalJvmCache.InlineFunctionsMap.process] is written only to handle *inline*
|
||||||
|
* property accessors (and functions).
|
||||||
|
*/
|
||||||
|
fun ProtoData.getNonPrivateMemberNames(includeInlineAccessors: Boolean): List<String> {
|
||||||
|
return when (this) {
|
||||||
|
is ClassProtoData -> {
|
||||||
|
getNonPrivateMembers() + (if (includeInlineAccessors) {
|
||||||
|
inlineAccessors(proto.propertyList, nameResolver, excludePrivateAccessors = true).map { it.name }
|
||||||
|
} else emptyList())
|
||||||
|
}
|
||||||
|
is PackagePartProtoData -> {
|
||||||
|
getNonPrivateMembers() + (if (includeInlineAccessors) {
|
||||||
|
inlineAccessors(proto.propertyList, nameResolver, excludePrivateAccessors = true).map { it.name }
|
||||||
|
} else emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal information about a Kotlin class to compute recompilation-triggering changes during an incremental run of the `KotlinCompile`
|
* Minimal information about a Kotlin class to compute recompilation-triggering changes during an incremental run of the `KotlinCompile`
|
||||||
* task (see [IncrementalJvmCache.saveClassToCache]).
|
* task (see [IncrementalJvmCache.saveClassToCache]).
|
||||||
@@ -674,7 +707,7 @@ class KotlinClassInfo constructor(
|
|||||||
val classHeaderStrings: Array<String>, // Can be empty
|
val classHeaderStrings: Array<String>, // Can be empty
|
||||||
val multifileClassName: String?, // Not null iff classKind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
val multifileClassName: String?, // Not null iff classKind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||||
val constantsMap: LinkedHashMap<String, Any>,
|
val constantsMap: LinkedHashMap<String, Any>,
|
||||||
val inlineFunctionsMap: LinkedHashMap<String, Long>
|
val inlineFunctionsAndAccessorsMap: LinkedHashMap<String, Long>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
|
val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
|
||||||
@@ -726,48 +759,55 @@ class KotlinClassInfo constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createFrom(classId: ClassId, classHeader: KotlinClassHeader, classContents: ByteArray): KotlinClassInfo {
|
fun createFrom(classId: ClassId, classHeader: KotlinClassHeader, classContents: ByteArray): KotlinClassInfo {
|
||||||
val constantsAndInlineFunctions = getConstantsAndInlineFunctions(classHeader, classContents)
|
val (constants, inlineFunctionsAndAccessors) = getConstantsAndInlineFunctionsOrAccessors(classHeader, classContents)
|
||||||
|
|
||||||
return KotlinClassInfo(
|
return KotlinClassInfo(
|
||||||
classId,
|
classId,
|
||||||
classHeader.kind,
|
classHeader.kind,
|
||||||
classHeader.data ?: classHeader.incompatibleData ?: emptyArray(),
|
classHeader.data ?: emptyArray(),
|
||||||
classHeader.strings ?: emptyArray(),
|
classHeader.strings ?: emptyArray(),
|
||||||
classHeader.multifileClassName,
|
classHeader.multifileClassName,
|
||||||
constantsMap = constantsAndInlineFunctions.first,
|
constants.mapKeysTo(LinkedHashMap()) { it.key.name },
|
||||||
inlineFunctionsMap = constantsAndInlineFunctions.second
|
inlineFunctionsAndAccessors.mapKeysTo(LinkedHashMap()) { it.key.asString() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses the class file only once to get both constants and inline functions. */
|
/**
|
||||||
private fun getConstantsAndInlineFunctions(
|
* Parses the class file only once to get both constants and inline functions/property accessors. This is faster than getting them
|
||||||
|
* separately in two passes.
|
||||||
|
*/
|
||||||
|
private fun getConstantsAndInlineFunctionsOrAccessors(
|
||||||
classHeader: KotlinClassHeader,
|
classHeader: KotlinClassHeader,
|
||||||
classContents: ByteArray
|
classContents: ByteArray
|
||||||
): Pair<LinkedHashMap<String, Any>, LinkedHashMap<String, Long>> {
|
): Pair<Map<JvmMemberSignature.Field, Any>, Map<JvmMemberSignature.Method, Long>> {
|
||||||
val constantsClassVisitor = ConstantsClassVisitor()
|
val constantsClassVisitor = ConstantsClassVisitor()
|
||||||
val inlineFunctionSignatures = inlineFunctionsJvmNames(classHeader)
|
val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(classHeader)
|
||||||
|
|
||||||
return if (inlineFunctionSignatures.isEmpty()) {
|
return if (inlineFunctionsAndAccessors.isEmpty()) {
|
||||||
ClassReader(classContents).accept(constantsClassVisitor, SKIP_CODE or SKIP_DEBUG or SKIP_FRAMES)
|
// parsingOptions = (SKIP_CODE, SKIP_DEBUG) as method bodies and debug info are not important for constants
|
||||||
Pair(constantsClassVisitor.getResult(), LinkedHashMap())
|
ClassReader(classContents).accept(constantsClassVisitor, SKIP_CODE or SKIP_DEBUG)
|
||||||
|
Pair(constantsClassVisitor.getResult(), emptyMap())
|
||||||
} else {
|
} else {
|
||||||
val inlineFunctionsClassVisitor = InlineFunctionsClassVisitor(inlineFunctionSignatures, constantsClassVisitor)
|
val inlineFunctionsAndAccessorsClassVisitor =
|
||||||
ClassReader(classContents).accept(inlineFunctionsClassVisitor, 0)
|
InlineFunctionsAndAccessorsClassVisitor(inlineFunctionsAndAccessors.toSet(), constantsClassVisitor)
|
||||||
Pair(constantsClassVisitor.getResult(), inlineFunctionsClassVisitor.getResult())
|
// parsingOptions must not include (SKIP_CODE, SKIP_DEBUG) as method bodies and debug info (e.g., line numbers) are important for
|
||||||
|
// inline functions/accessors
|
||||||
|
ClassReader(classContents).accept(inlineFunctionsAndAccessorsClassVisitor, 0)
|
||||||
|
Pair(constantsClassVisitor.getResult(), inlineFunctionsAndAccessorsClassVisitor.getResult())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ConstantsClassVisitor : ClassVisitor(Opcodes.API_VERSION) {
|
private class ConstantsClassVisitor : ClassVisitor(Opcodes.API_VERSION) {
|
||||||
private val result = LinkedHashMap<String, Any>()
|
private val result = mutableMapOf<JvmMemberSignature.Field, Any>()
|
||||||
|
|
||||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||||
if (access and Opcodes.ACC_PRIVATE == Opcodes.ACC_PRIVATE) return null
|
if (access and Opcodes.ACC_PRIVATE == Opcodes.ACC_PRIVATE) return null
|
||||||
|
|
||||||
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL
|
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL
|
||||||
if (value != null && access and staticFinal == staticFinal) {
|
if (value != null && access and staticFinal == staticFinal) {
|
||||||
result[name] = value
|
result[JvmMemberSignature.Field(name, desc)] = value
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -775,12 +815,12 @@ private class ConstantsClassVisitor : ClassVisitor(Opcodes.API_VERSION) {
|
|||||||
fun getResult() = result
|
fun getResult() = result
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InlineFunctionsClassVisitor(
|
private class InlineFunctionsAndAccessorsClassVisitor(
|
||||||
private val inlineFunctionSignatures: Set<String>,
|
private val inlineFunctionsAndAccessors: Set<JvmMemberSignature.Method>,
|
||||||
cv: ConstantsClassVisitor // Note: cv must not override the visitMethod (it will not be called with the current implementation below)
|
cv: ConstantsClassVisitor // Note: cv must not override `visitMethod` (it will not be called with the current implementation below)
|
||||||
) : ClassVisitor(Opcodes.API_VERSION, cv) {
|
) : ClassVisitor(Opcodes.API_VERSION, cv) {
|
||||||
|
|
||||||
private val result = LinkedHashMap<String, Long>()
|
private val result = mutableMapOf<JvmMemberSignature.Method, Long>()
|
||||||
private var classVersion: Int? = null
|
private var classVersion: Int? = null
|
||||||
|
|
||||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
|
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
|
||||||
@@ -791,11 +831,8 @@ private class InlineFunctionsClassVisitor(
|
|||||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||||
if (access and Opcodes.ACC_PRIVATE == Opcodes.ACC_PRIVATE) return null
|
if (access and Opcodes.ACC_PRIVATE == Opcodes.ACC_PRIVATE) return null
|
||||||
|
|
||||||
// Note: Here, functionSignature = name + descriptor.
|
val method = JvmMemberSignature.Method(name, desc)
|
||||||
// It is different from the `signature` parameter above, which is essentially a more detailed descriptor when generics are used
|
if (method !in inlineFunctionsAndAccessors) return null
|
||||||
// (or null otherwise).
|
|
||||||
val functionSignature = JvmMemberSignature.Method(name, desc).asString()
|
|
||||||
if (functionSignature !in inlineFunctionSignatures) return null
|
|
||||||
|
|
||||||
val classWriter = ClassWriter(0)
|
val classWriter = ClassWriter(0)
|
||||||
|
|
||||||
@@ -804,7 +841,7 @@ private class InlineFunctionsClassVisitor(
|
|||||||
|
|
||||||
return object : MethodVisitor(Opcodes.API_VERSION, classWriter.visitMethod(access, name, desc, signature, exceptions)) {
|
return object : MethodVisitor(Opcodes.API_VERSION, classWriter.visitMethod(access, name, desc, signature, exceptions)) {
|
||||||
override fun visitEnd() {
|
override fun visitEnd() {
|
||||||
result[functionSignature] = classWriter.toByteArray().md5()
|
result[method] = classWriter.toByteArray().md5()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,17 +51,17 @@ fun ProtoMapValue.toProtoData(packageFqName: FqName): ProtoData =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val MessageLite.isPrivate: Boolean
|
internal val MessageLite.isPrivate: Boolean
|
||||||
get() = DescriptorVisibilities.isPrivate(
|
get() {
|
||||||
ProtoEnumFlags.descriptorVisibility(
|
val visibility = when (this) {
|
||||||
when (this) {
|
is ProtoBuf.Constructor -> Flags.VISIBILITY.get(flags)
|
||||||
is ProtoBuf.Constructor -> Flags.VISIBILITY.get(flags)
|
is ProtoBuf.Function -> Flags.VISIBILITY.get(flags)
|
||||||
is ProtoBuf.Function -> Flags.VISIBILITY.get(flags)
|
is ProtoBuf.Property -> Flags.VISIBILITY.get(flags)
|
||||||
is ProtoBuf.Property -> Flags.VISIBILITY.get(flags)
|
is ProtoBuf.TypeAlias -> Flags.VISIBILITY.get(flags)
|
||||||
is ProtoBuf.TypeAlias -> Flags.VISIBILITY.get(flags)
|
is ProtoBuf.EnumEntry -> return false // EnumEntry doesn't have a visibility flag
|
||||||
else -> error("Unknown message: $this")
|
else -> error("Unknown message: $this")
|
||||||
}
|
}
|
||||||
)
|
return DescriptorVisibilities.isPrivate(ProtoEnumFlags.descriptorVisibility(visibility))
|
||||||
)
|
}
|
||||||
|
|
||||||
private fun MessageLite.name(nameResolver: NameResolver): String {
|
private fun MessageLite.name(nameResolver: NameResolver): String {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
@@ -69,6 +69,7 @@ private fun MessageLite.name(nameResolver: NameResolver): String {
|
|||||||
is ProtoBuf.Function -> nameResolver.getString(name)
|
is ProtoBuf.Function -> nameResolver.getString(name)
|
||||||
is ProtoBuf.Property -> nameResolver.getString(name)
|
is ProtoBuf.Property -> nameResolver.getString(name)
|
||||||
is ProtoBuf.TypeAlias -> nameResolver.getString(name)
|
is ProtoBuf.TypeAlias -> nameResolver.getString(name)
|
||||||
|
is ProtoBuf.EnumEntry -> nameResolver.getString(name)
|
||||||
else -> error("Unknown message: $this")
|
else -> error("Unknown message: $this")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,6 +308,25 @@ class DifferenceCalculatorForClass(
|
|||||||
|
|
||||||
return Difference(isClassAffected, areSubclassesAffected, names, changedSupertypes)
|
return Difference(isClassAffected, areSubclassesAffected, names, changedSupertypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun ClassProtoData.getNonPrivateMembers(): List<String> {
|
||||||
|
val membersResolvers: List<(ProtoBuf.Class) -> List<MessageLite>> = listOf(
|
||||||
|
// This list must match the logic in `DifferenceCalculatorForClass.difference`
|
||||||
|
// TODO: Consider adding COMPANION_OBJECT_NAME and NESTED_CLASS_NAME_LIST as they are also members of a class (see
|
||||||
|
// `DifferenceCalculatorForClass.difference`)
|
||||||
|
ProtoBuf.Class::getConstructorList,
|
||||||
|
ProtoBuf.Class::getFunctionList,
|
||||||
|
ProtoBuf.Class::getPropertyList,
|
||||||
|
ProtoBuf.Class::getTypeAliasList,
|
||||||
|
ProtoBuf.Class::getEnumEntryList
|
||||||
|
)
|
||||||
|
return membersResolvers.flatMap { membersResolver ->
|
||||||
|
membersResolver(proto).filterNot { it.isPrivate }.names(nameResolver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DifferenceCalculatorForPackageFacade(
|
class DifferenceCalculatorForPackageFacade(
|
||||||
@@ -362,6 +382,21 @@ class DifferenceCalculatorForPackageFacade(
|
|||||||
|
|
||||||
return Difference(changedMembersNames = names)
|
return Difference(changedMembersNames = names)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun PackagePartProtoData.getNonPrivateMembers(): List<String> {
|
||||||
|
val membersResolvers: List<(ProtoBuf.Package) -> List<MessageLite>> = listOf(
|
||||||
|
// This list must match the logic in `DifferenceCalculatorForPackageFacade.difference`
|
||||||
|
ProtoBuf.Package::getFunctionList,
|
||||||
|
ProtoBuf.Package::getPropertyList,
|
||||||
|
ProtoBuf.Package::getTypeAliasList
|
||||||
|
)
|
||||||
|
return membersResolvers.flatMap { membersResolver ->
|
||||||
|
membersResolver(proto).filterNot { it.isPrivate }.names(nameResolver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ProtoBuf.Class.isSealed: Boolean
|
private val ProtoBuf.Class.isSealed: Boolean
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.inline
|
package org.jetbrains.kotlin.inline
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||||
@@ -24,57 +25,68 @@ import org.jetbrains.kotlin.metadata.deserialization.TypeTable
|
|||||||
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
|
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
|
||||||
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
||||||
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf.JvmMethodSignature
|
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf.JvmMethodSignature
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature
|
||||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags
|
||||||
|
import org.jetbrains.kotlin.serialization.deserialization.descriptorVisibility
|
||||||
|
|
||||||
fun inlineFunctionsJvmNames(header: KotlinClassHeader): Set<String> {
|
fun inlineFunctionsAndAccessors(header: KotlinClassHeader): List<JvmMemberSignature.Method> {
|
||||||
val annotationData = header.data ?: return emptySet()
|
val data = header.data ?: return emptyList()
|
||||||
val strings = header.strings ?: return emptySet()
|
val strings = header.strings ?: return emptyList()
|
||||||
|
|
||||||
return when (header.kind) {
|
return when (header.kind) {
|
||||||
KotlinClassHeader.Kind.CLASS -> {
|
KotlinClassHeader.Kind.CLASS -> {
|
||||||
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(annotationData, strings)
|
val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(data, strings)
|
||||||
inlineFunctionsJvmNames(classProto.functionList, nameResolver, classProto.typeTable) +
|
inlineFunctions(classProto.functionList, nameResolver, classProto.typeTable) +
|
||||||
inlineAccessorsJvmNames(classProto.propertyList, nameResolver)
|
inlineAccessors(classProto.propertyList, nameResolver)
|
||||||
}
|
}
|
||||||
KotlinClassHeader.Kind.FILE_FACADE,
|
KotlinClassHeader.Kind.FILE_FACADE,
|
||||||
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
||||||
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(annotationData, strings)
|
val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings)
|
||||||
inlineFunctionsJvmNames(packageProto.functionList, nameResolver, packageProto.typeTable) +
|
inlineFunctions(packageProto.functionList, nameResolver, packageProto.typeTable) +
|
||||||
inlineAccessorsJvmNames(packageProto.propertyList, nameResolver)
|
inlineAccessors(packageProto.propertyList, nameResolver)
|
||||||
}
|
}
|
||||||
else -> emptySet()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inlineFunctionsJvmNames(functions: List<ProtoBuf.Function>, nameResolver: NameResolver, protoTypeTable: ProtoBuf.TypeTable): Set<String> {
|
private fun inlineFunctions(
|
||||||
|
functions: List<ProtoBuf.Function>,
|
||||||
|
nameResolver: NameResolver,
|
||||||
|
protoTypeTable: ProtoBuf.TypeTable
|
||||||
|
): List<JvmMemberSignature.Method> {
|
||||||
val typeTable = TypeTable(protoTypeTable)
|
val typeTable = TypeTable(protoTypeTable)
|
||||||
val inlineFunctions = functions.filter { Flags.IS_INLINE.get(it.flags) }
|
return functions.filter { Flags.IS_INLINE.get(it.flags) }.mapNotNull {
|
||||||
val jvmNames = inlineFunctions.mapNotNull {
|
JvmProtoBufUtil.getJvmMethodSignature(it, nameResolver, typeTable)
|
||||||
JvmProtoBufUtil.getJvmMethodSignature(it, nameResolver, typeTable)?.asString()
|
|
||||||
}
|
}
|
||||||
return jvmNames.toSet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inlineAccessorsJvmNames(properties: List<ProtoBuf.Property>, nameResolver: NameResolver): Set<String> {
|
fun inlineAccessors(
|
||||||
val propertiesWithInlineAccessors = properties.filter { proto ->
|
properties: List<ProtoBuf.Property>,
|
||||||
proto.hasGetterFlags() && Flags.IS_INLINE_ACCESSOR.get(proto.getterFlags) ||
|
nameResolver: NameResolver,
|
||||||
proto.hasSetterFlags() && Flags.IS_INLINE_ACCESSOR.get(proto.setterFlags)
|
excludePrivateAccessors: Boolean = false
|
||||||
}
|
): List<JvmMemberSignature.Method> {
|
||||||
val inlineAccessors = arrayListOf<JvmMethodSignature>()
|
val inlineAccessors = mutableListOf<JvmMethodSignature>()
|
||||||
propertiesWithInlineAccessors.forEach { proto ->
|
|
||||||
val signature = proto.getExtensionOrNull(JvmProtoBuf.propertySignature)
|
|
||||||
if (signature != null) {
|
|
||||||
if (proto.hasGetterFlags() && Flags.IS_INLINE_ACCESSOR.get(proto.getterFlags)) {
|
|
||||||
inlineAccessors.add(signature.getter)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.hasSetterFlags() && Flags.IS_INLINE_ACCESSOR.get(proto.setterFlags)) {
|
fun isInline(flags: Int) = Flags.IS_INLINE_ACCESSOR.get(flags)
|
||||||
inlineAccessors.add(signature.setter)
|
fun isPrivate(flags: Int) = DescriptorVisibilities.isPrivate(ProtoEnumFlags.descriptorVisibility(Flags.VISIBILITY.get(flags)))
|
||||||
|
|
||||||
|
properties.forEach { property ->
|
||||||
|
val propertySignature = property.getExtensionOrNull(JvmProtoBuf.propertySignature) ?: return@forEach
|
||||||
|
|
||||||
|
if (property.hasGetterFlags() && isInline(property.getterFlags)) {
|
||||||
|
if (!(excludePrivateAccessors && isPrivate(property.getterFlags))) {
|
||||||
|
inlineAccessors.add(propertySignature.getter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (property.hasSetterFlags() && isInline(property.setterFlags)) {
|
||||||
|
if (!(excludePrivateAccessors && isPrivate(property.setterFlags))) {
|
||||||
|
inlineAccessors.add(propertySignature.setter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return inlineAccessors.map {
|
return inlineAccessors.map {
|
||||||
nameResolver.getString(it.name) + nameResolver.getString(it.desc)
|
JvmMemberSignature.Method(name = nameResolver.getString(it.name), desc = nameResolver.getString(it.desc))
|
||||||
}.toSet()
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
import org.jetbrains.kotlin.incremental.ChangesCollector.Companion.getNonPrivateMemberNames
|
|
||||||
import org.jetbrains.kotlin.metadata.ProtoBuf.Visibility.PRIVATE
|
import org.jetbrains.kotlin.metadata.ProtoBuf.Visibility.PRIVATE
|
||||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -98,7 +97,8 @@ class AbiSnapshotDiffService() {
|
|||||||
when (protoData) {
|
when (protoData) {
|
||||||
is ClassProtoData -> {
|
is ClassProtoData -> {
|
||||||
fqNames.add(fqName)
|
fqNames.add(fqName)
|
||||||
symbols.addAll(protoData.getNonPrivateMemberNames().map { LookupSymbol(it, fqName.asString()) })
|
symbols.addAll(
|
||||||
|
protoData.getNonPrivateMemberNames(includeInlineAccessors = true).map { LookupSymbol(it, fqName.asString()) })
|
||||||
}
|
}
|
||||||
is PackagePartProtoData -> {
|
is PackagePartProtoData -> {
|
||||||
symbols.addAll(
|
symbols.addAll(
|
||||||
|
|||||||
+4
-2
@@ -12,7 +12,8 @@ import org.jetbrains.kotlin.name.ClassId
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader.*
|
import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_CODE
|
||||||
|
import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG
|
||||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
|
||||||
@@ -47,7 +48,8 @@ class BasicClassInfo(
|
|||||||
val innerClassesClassVisitor = InnerClassesClassVisitor(kotlinClassHeaderClassVisitor)
|
val innerClassesClassVisitor = InnerClassesClassVisitor(kotlinClassHeaderClassVisitor)
|
||||||
val basicClassInfoVisitor = BasicClassInfoClassVisitor(innerClassesClassVisitor)
|
val basicClassInfoVisitor = BasicClassInfoClassVisitor(innerClassesClassVisitor)
|
||||||
|
|
||||||
ClassReader(classContents).accept(basicClassInfoVisitor, SKIP_CODE or SKIP_DEBUG or SKIP_FRAMES)
|
// parsingOptions = (SKIP_CODE, SKIP_DEBUG) as method bodies and debug info are not important
|
||||||
|
ClassReader(classContents).accept(basicClassInfoVisitor, SKIP_CODE or SKIP_DEBUG)
|
||||||
|
|
||||||
val className = basicClassInfoVisitor.getClassName()
|
val className = basicClassInfoVisitor.getClassName()
|
||||||
val innerClassesInfo = innerClassesClassVisitor.getInnerClassesInfo()
|
val innerClassesInfo = innerClassesClassVisitor.getInnerClassesInfo()
|
||||||
|
|||||||
+2
-2
@@ -186,7 +186,7 @@ internal object KotlinClassInfoExternalizer : DataExternalizer<KotlinClassInfo>
|
|||||||
ListExternalizer(StringExternalizer).save(output, info.classHeaderStrings.toList())
|
ListExternalizer(StringExternalizer).save(output, info.classHeaderStrings.toList())
|
||||||
NullableValueExternalizer(StringExternalizer).save(output, info.multifileClassName)
|
NullableValueExternalizer(StringExternalizer).save(output, info.multifileClassName)
|
||||||
LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).save(output, info.constantsMap)
|
LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).save(output, info.constantsMap)
|
||||||
LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).save(output, info.inlineFunctionsMap)
|
LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).save(output, info.inlineFunctionsAndAccessorsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(input: DataInput): KotlinClassInfo {
|
override fun read(input: DataInput): KotlinClassInfo {
|
||||||
@@ -198,7 +198,7 @@ internal object KotlinClassInfoExternalizer : DataExternalizer<KotlinClassInfo>
|
|||||||
classHeaderStrings = ListExternalizer(StringExternalizer).read(input).toTypedArray(),
|
classHeaderStrings = ListExternalizer(StringExternalizer).read(input).toTypedArray(),
|
||||||
multifileClassName = NullableValueExternalizer(StringExternalizer).read(input),
|
multifileClassName = NullableValueExternalizer(StringExternalizer).read(input),
|
||||||
constantsMap = LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).read(input),
|
constantsMap = LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).read(input),
|
||||||
inlineFunctionsMap = LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).read(input)
|
inlineFunctionsAndAccessorsMap = LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).read(input)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -9,10 +9,9 @@ import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
|
|||||||
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
import org.jetbrains.kotlin.build.report.metrics.BuildTime
|
||||||
import org.jetbrains.kotlin.build.report.metrics.DoNothingBuildMetricsReporter
|
import org.jetbrains.kotlin.build.report.metrics.DoNothingBuildMetricsReporter
|
||||||
import org.jetbrains.kotlin.build.report.metrics.measure
|
import org.jetbrains.kotlin.build.report.metrics.measure
|
||||||
import org.jetbrains.kotlin.incremental.ChangesCollector.Companion.getNonPrivateMemberNames
|
|
||||||
import org.jetbrains.kotlin.incremental.KotlinClassInfo
|
import org.jetbrains.kotlin.incremental.KotlinClassInfo
|
||||||
import org.jetbrains.kotlin.incremental.PackagePartProtoData
|
|
||||||
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_MEMBER_LEVEL
|
import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_MEMBER_LEVEL
|
||||||
|
import org.jetbrains.kotlin.incremental.getNonPrivateMemberNames
|
||||||
import org.jetbrains.kotlin.incremental.md5
|
import org.jetbrains.kotlin.incremental.md5
|
||||||
import org.jetbrains.kotlin.incremental.storage.toByteArray
|
import org.jetbrains.kotlin.incremental.storage.toByteArray
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.*
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.*
|
||||||
@@ -97,7 +96,7 @@ object ClassSnapshotter {
|
|||||||
)
|
)
|
||||||
FILE_FACADE, MULTIFILE_CLASS_PART -> PackageFacadeKotlinClassSnapshot(
|
FILE_FACADE, MULTIFILE_CLASS_PART -> PackageFacadeKotlinClassSnapshot(
|
||||||
classId, classAbiHash, classMemberLevelSnapshot,
|
classId, classAbiHash, classMemberLevelSnapshot,
|
||||||
packageMemberNames = (kotlinClassInfo.protoData as PackagePartProtoData).getNonPrivateMemberNames()
|
packageMemberNames = kotlinClassInfo.protoData.getNonPrivateMemberNames(includeInlineAccessors = true).toSet()
|
||||||
)
|
)
|
||||||
MULTIFILE_CLASS -> MultifileClassKotlinClassSnapshot(
|
MULTIFILE_CLASS -> MultifileClassKotlinClassSnapshot(
|
||||||
classId, classAbiHash, classMemberLevelSnapshot,
|
classId, classAbiHash, classMemberLevelSnapshot,
|
||||||
|
|||||||
+5
-1
@@ -183,7 +183,10 @@ private object ConstantsInCompanionObjectsImpact : Impact {
|
|||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
}.toMap()
|
}.toMap()
|
||||||
val classToCompanionObject: Map<ClassId, ClassId> = companionObjectToConstants.keys.associateBy { it.parentClassId!! }
|
val classToCompanionObject: Map<ClassId, ClassId> = companionObjectToConstants.keys.associateBy { companionObject ->
|
||||||
|
// companionObject.parentClassId should be present in `allClasses` as this is a companion object
|
||||||
|
companionObject.parentClassId!!
|
||||||
|
}
|
||||||
|
|
||||||
return object : ImpactedSymbolsResolver {
|
return object : ImpactedSymbolsResolver {
|
||||||
override fun getImpactedClasses(classId: ClassId): Set<ClassId> {
|
override fun getImpactedClasses(classId: ClassId): Set<ClassId> {
|
||||||
@@ -211,6 +214,7 @@ private object ConstantsInCompanionObjectsImpact : Impact {
|
|||||||
return object : ImpactingClassesResolver {
|
return object : ImpactingClassesResolver {
|
||||||
override fun getImpactingClasses(classId: ClassId): Set<ClassId> {
|
override fun getImpactingClasses(classId: ClassId): Set<ClassId> {
|
||||||
return if (classId in companionObjects) {
|
return if (classId in companionObjects) {
|
||||||
|
// classId.parentClassId should be present in `allClasses` as this is a companion object
|
||||||
setOf(classId.parentClassId!!)
|
setOf(classId.parentClassId!!)
|
||||||
} else emptySet()
|
} else emptySet()
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -28,11 +28,11 @@ object JavaClassSnapshotter {
|
|||||||
|
|
||||||
// First, collect all info.
|
// First, collect all info.
|
||||||
// Note the parsing options passed to ClassReader:
|
// Note the parsing options passed to ClassReader:
|
||||||
// - SKIP_CODE and SKIP_FRAMES are set as method bodies will not be part of the ABI of the class.
|
// - SKIP_CODE is set as method bodies will not be part of the ABI of the class.
|
||||||
// - SKIP_DEBUG is not set as it would skip method parameters, which may be used by annotation processors like Room.
|
// - SKIP_DEBUG seems possible but is *not* set just to be safe, so that we don't skip any info that might be important.
|
||||||
// - EXPAND_FRAMES is not needed (and not relevant when SKIP_CODE is set).
|
// - SKIP_FRAMES or EXPAND_FRAMES is not needed (and not relevant when SKIP_CODE is set).
|
||||||
val classReader = ClassReader(classFile.contents)
|
val classReader = ClassReader(classFile.contents)
|
||||||
classReader.accept(abiClass, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES)
|
classReader.accept(abiClass, ClassReader.SKIP_CODE)
|
||||||
|
|
||||||
// Then, remove non-ABI info, which includes:
|
// Then, remove non-ABI info, which includes:
|
||||||
// - Method bodies: Should have already been removed (see SKIP_CODE above)
|
// - Method bodies: Should have already been removed (see SKIP_CODE above)
|
||||||
|
|||||||
+25
@@ -202,6 +202,7 @@ class KotlinOnlyClasspathChangesComputerTest : ClasspathChangesComputerTest() {
|
|||||||
|
|
||||||
LookupSymbol(name = "inlineFunctionChangedSignature", scope = "com.example.SomeClass"),
|
LookupSymbol(name = "inlineFunctionChangedSignature", scope = "com.example.SomeClass"),
|
||||||
LookupSymbol(name = "inlineFunctionChangedImplementation", scope = "com.example.SomeClass"),
|
LookupSymbol(name = "inlineFunctionChangedImplementation", scope = "com.example.SomeClass"),
|
||||||
|
LookupSymbol(name = "inlineFunctionChangedLineNumber", scope = "com.example.SomeClass"),
|
||||||
|
|
||||||
LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass"),
|
LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass"),
|
||||||
LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass.CompanionObject"),
|
LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass.CompanionObject"),
|
||||||
@@ -213,6 +214,30 @@ class KotlinOnlyClasspathChangesComputerTest : ClasspathChangesComputerTest() {
|
|||||||
).assertEquals(changes)
|
).assertEquals(changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPropertyAccessors() {
|
||||||
|
val changes = computeClasspathChanges(File(testDataDir, "KotlinOnly/testPropertyAccessors/src"), tmpDir)
|
||||||
|
Changes(
|
||||||
|
lookupSymbols = setOf(
|
||||||
|
LookupSymbol(name = "property_ChangedType", scope = "com.example.SomeClass"),
|
||||||
|
|
||||||
|
LookupSymbol(name = "inlineProperty_ChangedType", scope = "com.example"),
|
||||||
|
LookupSymbol(name = "inlineProperty_ChangedType_BackingField", scope = "com.example"),
|
||||||
|
LookupSymbol(name = "getInlineProperty_ChangedType", scope = "com.example"),
|
||||||
|
LookupSymbol(name = "setInlineProperty_ChangedType", scope = "com.example"),
|
||||||
|
|
||||||
|
LookupSymbol(name = "getInlineProperty_ChangedGetterImpl", scope = "com.example"),
|
||||||
|
LookupSymbol(name = "setInlineProperty_ChangedSetterImpl", scope = "com.example"),
|
||||||
|
|
||||||
|
LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass")
|
||||||
|
),
|
||||||
|
fqNames = setOf(
|
||||||
|
"com.example",
|
||||||
|
"com.example.SomeClass"
|
||||||
|
)
|
||||||
|
).assertEquals(changes)
|
||||||
|
}
|
||||||
|
|
||||||
/** Tests [SupertypesInheritorsImpact]. */
|
/** Tests [SupertypesInheritorsImpact]. */
|
||||||
@Test
|
@Test
|
||||||
override fun testImpactComputation_SupertypesInheritors() {
|
override fun testImpactComputation_SupertypesInheritors() {
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+2
@@ -14,6 +14,8 @@ class SomeClass {
|
|||||||
|
|
||||||
inline fun inlineFunctionChangedSignature(): Long = 0
|
inline fun inlineFunctionChangedSignature(): Long = 0
|
||||||
inline fun inlineFunctionChangedImplementation(): Int = 1000
|
inline fun inlineFunctionChangedImplementation(): Int = 1000
|
||||||
|
|
||||||
|
inline fun inlineFunctionChangedLineNumber(): Int = 0
|
||||||
inline fun inlineFunctionChangedUnchanged(): Int = 0
|
inline fun inlineFunctionChangedUnchanged(): Int = 0
|
||||||
private inline fun privateInlineFunctionChangedSignature(): Long = 0
|
private inline fun privateInlineFunctionChangedSignature(): Long = 0
|
||||||
}
|
}
|
||||||
+2
@@ -14,6 +14,8 @@ class SomeClass {
|
|||||||
|
|
||||||
inline fun inlineFunctionChangedSignature(): Int = 0
|
inline fun inlineFunctionChangedSignature(): Int = 0
|
||||||
inline fun inlineFunctionChangedImplementation(): Int = 0
|
inline fun inlineFunctionChangedImplementation(): Int = 0
|
||||||
|
inline fun inlineFunctionChangedLineNumber(): Int = 0
|
||||||
|
|
||||||
inline fun inlineFunctionChangedUnchanged(): Int = 0
|
inline fun inlineFunctionChangedUnchanged(): Int = 0
|
||||||
private inline fun privateInlineFunctionChangedSignature(): Int = 0
|
private inline fun privateInlineFunctionChangedSignature(): Int = 0
|
||||||
}
|
}
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+79
@@ -0,0 +1,79 @@
|
|||||||
|
package com.example
|
||||||
|
|
||||||
|
@Suppress("RedundantGetter", "RedundantSetter")
|
||||||
|
class SomeClass {
|
||||||
|
|
||||||
|
var property_ChangedType: Long = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var property_ChangedGetterImpl: Int = 0
|
||||||
|
get() {
|
||||||
|
println("Getter implementation has changed!")
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var property_ChangedSetterImpl: Int = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
println("Setter implementation has changed!")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var property_Unchanged: Int = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private var privateProperty_ChangedType: Long = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inlineProperty_ChangedType_BackingField: Long = 0
|
||||||
|
var inlineProperty_ChangedGetterImpl_BackingField: Int = 0
|
||||||
|
var inlineProperty_ChangedSetterImpl_BackingField: Int = 0
|
||||||
|
var inlineProperty_Unchanged_BackingField: Int = 0
|
||||||
|
private var privateInlineProperty_ChangedType_BackingField: Long = 0
|
||||||
|
|
||||||
|
inline var inlineProperty_ChangedType: Long
|
||||||
|
get() = inlineProperty_ChangedType_BackingField
|
||||||
|
set(value) {
|
||||||
|
inlineProperty_ChangedType_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
inline var inlineProperty_ChangedGetterImpl: Int
|
||||||
|
get() {
|
||||||
|
println("Getter implementation has changed!")
|
||||||
|
return inlineProperty_ChangedGetterImpl_BackingField
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
inlineProperty_ChangedGetterImpl_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
inline var inlineProperty_ChangedSetterImpl: Int
|
||||||
|
get() = inlineProperty_ChangedSetterImpl_BackingField
|
||||||
|
set(value) {
|
||||||
|
println("Setter implementation has changed!")
|
||||||
|
inlineProperty_ChangedSetterImpl_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
inline var inlineProperty_Unchanged: Int
|
||||||
|
get() = inlineProperty_Unchanged_BackingField
|
||||||
|
set(value) {
|
||||||
|
inlineProperty_Unchanged_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline var privateInlineProperty_ChangedType: Long
|
||||||
|
get() = privateInlineProperty_ChangedType_BackingField
|
||||||
|
set(value) {
|
||||||
|
privateInlineProperty_ChangedType_BackingField = value
|
||||||
|
}
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
package com.example
|
||||||
|
|
||||||
|
@Suppress("RedundantGetter", "RedundantSetter")
|
||||||
|
class SomeClass {
|
||||||
|
|
||||||
|
var property_ChangedType: Int = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var property_ChangedGetterImpl: Int = 0
|
||||||
|
get() {
|
||||||
|
println("Getter implementation")
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var property_ChangedSetterImpl: Int = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
println("Setter implementation")
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var property_Unchanged: Int = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private var privateProperty_ChangedType: Int = 0
|
||||||
|
get() = field
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inlineProperty_ChangedType_BackingField: Int = 0
|
||||||
|
var inlineProperty_ChangedGetterImpl_BackingField: Int = 0
|
||||||
|
var inlineProperty_ChangedSetterImpl_BackingField: Int = 0
|
||||||
|
var inlineProperty_Unchanged_BackingField: Int = 0
|
||||||
|
private var privateInlineProperty_ChangedType_BackingField: Int = 0
|
||||||
|
|
||||||
|
inline var inlineProperty_ChangedType: Int
|
||||||
|
get() = inlineProperty_ChangedType_BackingField
|
||||||
|
set(value) {
|
||||||
|
inlineProperty_ChangedType_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
inline var inlineProperty_ChangedGetterImpl: Int
|
||||||
|
get() {
|
||||||
|
println("Getter implementation")
|
||||||
|
return inlineProperty_ChangedGetterImpl_BackingField
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
inlineProperty_ChangedGetterImpl_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
inline var inlineProperty_ChangedSetterImpl: Int
|
||||||
|
get() = inlineProperty_ChangedSetterImpl_BackingField
|
||||||
|
set(value) {
|
||||||
|
println("Setter implementation")
|
||||||
|
inlineProperty_ChangedSetterImpl_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
inline var inlineProperty_Unchanged: Int
|
||||||
|
get() = inlineProperty_Unchanged_BackingField
|
||||||
|
set(value) {
|
||||||
|
inlineProperty_Unchanged_BackingField = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline var privateInlineProperty_ChangedType: Int
|
||||||
|
get() = privateInlineProperty_ChangedType_BackingField
|
||||||
|
set(value) {
|
||||||
|
privateInlineProperty_ChangedType_BackingField = value
|
||||||
|
}
|
||||||
+1
-1
@@ -44,7 +44,7 @@
|
|||||||
"publicFunction"
|
"publicFunction"
|
||||||
],
|
],
|
||||||
"constantsMap": {},
|
"constantsMap": {},
|
||||||
"inlineFunctionsMap": {},
|
"inlineFunctionsAndAccessorsMap": {},
|
||||||
"className$delegate": {
|
"className$delegate": {
|
||||||
"initializer": {},
|
"initializer": {},
|
||||||
"_value": {}
|
"_value": {}
|
||||||
|
|||||||
+6
-6
@@ -18,15 +18,15 @@ package org.jetbrains.kotlin.codegen
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
|
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
|
||||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
import org.jetbrains.kotlin.inline.inlineFunctionsAndAccessors
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object InlineTestUtil {
|
object InlineTestUtil {
|
||||||
fun checkNoCallsToInline(
|
fun checkNoCallsToInline(
|
||||||
@@ -57,13 +57,13 @@ object InlineTestUtil {
|
|||||||
val binaryClasses = hashMapOf<String, KotlinJvmBinaryClass>()
|
val binaryClasses = hashMapOf<String, KotlinJvmBinaryClass>()
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
val binaryClass = loadBinaryClass(file)
|
val binaryClass = loadBinaryClass(file)
|
||||||
val inlineFunctions = inlineFunctionsJvmNames(binaryClass.classHeader)
|
val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader)
|
||||||
|
|
||||||
val classVisitor = object : ClassVisitorWithName() {
|
val classVisitor = object : ClassVisitorWithName() {
|
||||||
override fun visitMethod(
|
override fun visitMethod(
|
||||||
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
|
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
|
||||||
): MethodVisitor? {
|
): MethodVisitor? {
|
||||||
if (name + desc in inlineFunctions) {
|
if (JvmMemberSignature.Method(name, desc) in inlineFunctionsAndAccessors) {
|
||||||
inlineMethods.add(MethodInfo(className, name, desc))
|
inlineMethods.add(MethodInfo(className, name, desc))
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@@ -81,14 +81,14 @@ object InlineTestUtil {
|
|||||||
var doLambdaInliningCheck = true
|
var doLambdaInliningCheck = true
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
val binaryClass = loadBinaryClass(file)
|
val binaryClass = loadBinaryClass(file)
|
||||||
val inlineFunctions = inlineFunctionsJvmNames(binaryClass.classHeader)
|
val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader)
|
||||||
|
|
||||||
//if inline function creates anonymous object then do not try to check that all lambdas are inlined
|
//if inline function creates anonymous object then do not try to check that all lambdas are inlined
|
||||||
val classVisitor = object : ClassVisitorWithName() {
|
val classVisitor = object : ClassVisitorWithName() {
|
||||||
override fun visitMethod(
|
override fun visitMethod(
|
||||||
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
|
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
|
||||||
): MethodVisitor? {
|
): MethodVisitor? {
|
||||||
if (name + desc in inlineFunctions) {
|
if (JvmMemberSignature.Method(name, desc) in inlineFunctionsAndAccessors) {
|
||||||
return object : MethodNodeWithAnonymousObjectCheck(inlineInfo, access, name, desc, signature, exceptions) {
|
return object : MethodNodeWithAnonymousObjectCheck(inlineInfo, access, name, desc, signature, exceptions) {
|
||||||
override fun onAnonymousConstructorCallOrSingletonAccess(owner: String) {
|
override fun onAnonymousConstructorCallOrSingletonAccess(owner: String) {
|
||||||
doLambdaInliningCheck = false
|
doLambdaInliningCheck = false
|
||||||
|
|||||||
Reference in New Issue
Block a user