From 9b212cfa8637c876171509adb2b11e8c85a70342 Mon Sep 17 00:00:00 2001 From: "nataliya.valtman" Date: Mon, 7 Nov 2022 13:59:32 +0100 Subject: [PATCH] Revert "Extract KotlinClassInfo to a separate class" This reverts commit ec3da62672877cbb6f4623537efbfc57004cd7cb. --- .../kotlin/incremental/ChangesCollector.kt | 8 +- .../kotlin/incremental/IncrementalJvmCache.kt | 243 +++++++++++++++--- .../kotlin/incremental/KotlinClassInfo.kt | 206 --------------- .../incremental/storage/externalizers.kt | 138 ++++------ .../org/jetbrains/kotlin/inline/inlineUtil.kt | 90 +++---- .../incremental/AbiSnapshotDiffService.kt | 4 +- .../incremental/IncrementalCompilerRunner.kt | 42 ++- .../IncrementalJvmCompilerRunner.kt | 6 +- .../ClasspathSnapshotSerializer.kt | 51 +++- .../classpathDiff/ClasspathSnapshotter.kt | 5 +- .../ClasspathChangesComputerTest.kt | 26 +- .../com/example/SomeClass.class | Bin 841 -> 0 bytes .../com/example/SomeClassKt.class | Bin 812 -> 0 bytes .../com/example/SomeClass.class | Bin 841 -> 0 bytes .../com/example/SomeClassKt.class | Bin 812 -> 0 bytes .../com/example/SomeClass.kt | 20 -- .../com/example/SomeClass.kt | 20 -- .../kotlin/codegen/InlineTestUtil.kt | 4 +- .../kotlin/benchmarks/GenerateIrRuntime.kt | 3 +- 19 files changed, 365 insertions(+), 501 deletions(-) delete mode 100644 build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt delete mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClass.class delete mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClassKt.class delete mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/previous-classpath/com/example/SomeClass.class delete mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/previous-classpath/com/example/SomeClassKt.class delete mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/src/current-classpath/com/example/SomeClass.kt delete mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/src/previous-classpath/com/example/SomeClass.kt diff --git a/build-common/src/org/jetbrains/kotlin/incremental/ChangesCollector.kt b/build-common/src/org/jetbrains/kotlin/incremental/ChangesCollector.kt index 9603244c671..d28e0ce336b 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/ChangesCollector.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/ChangesCollector.kt @@ -16,8 +16,6 @@ package org.jetbrains.kotlin.incremental -import org.jetbrains.kotlin.incremental.DifferenceCalculatorForClass.Companion.getNonPrivateMembers -import org.jetbrains.kotlin.incremental.DifferenceCalculatorForPackageFacade.Companion.getNonPrivateMembers import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.NameResolver import org.jetbrains.kotlin.name.FqName @@ -168,7 +166,7 @@ class ChangesCollector { } private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) { - val memberNames = getNonPrivateMembers() + val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true) if (isRemoved) { collectRemovedMembers(packageFqName, memberNames) } else { @@ -180,14 +178,14 @@ class ChangesCollector { val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName() if (proto.isCompanionObject) { - val memberNames = getNonPrivateMembers() + val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true) val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember collectMember(classFqName.parent(), classFqName.shortName().asString()) memberNames.forEach { collectMember(classFqName, it) } } else { if (!isRemoved && collectAllMembersForNewClass) { - val memberNames = getNonPrivateMembers() + val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true) memberNames.forEach { this@ChangesCollector.collectChangedMember(classFqName, it) } } diff --git a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt index 7341a97bc60..41248f93b7e 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt @@ -23,20 +23,27 @@ import com.intellij.util.io.EnumeratorStringDescriptor import gnu.trove.THashSet import org.jetbrains.annotations.TestOnly 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.inline.InlineFunction -import org.jetbrains.kotlin.inline.InlineFunctionOrAccessor -import org.jetbrains.kotlin.inline.InlinePropertyAccessor +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.incremental.components.IncrementalCache import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.jvm.deserialization.BitEncoding +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.org.objectweb.asm.* +import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_CODE +import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG import java.io.File import java.security.MessageDigest @@ -432,7 +439,7 @@ open class IncrementalJvmCache( // todo: reuse code with InlineFunctionsMap? private inner class ConstantsMap(storageFile: File) : - BasicStringMap>(storageFile, MapExternalizer(StringExternalizer, ConstantValueExternalizer)) { + BasicStringMap>(storageFile, LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer)) { operator fun contains(className: JvmClassName): Boolean = className.internalName in storage @@ -480,7 +487,7 @@ open class IncrementalJvmCache( storage.remove(className.internalName) } - override fun dumpValue(value: Map): String = + override fun dumpValue(value: LinkedHashMap): String = value.dumpMap(Any::toString) } @@ -560,10 +567,7 @@ open class IncrementalJvmCache( } private inner class InlineFunctionsMap(storageFile: File) : - BasicStringMap>( - storageFile, - MapExternalizer(InlineFunctionOrAccessorExternalizer, LongExternalizer) - ) { + BasicStringMap>(storageFile, LinkedHashMapExternalizer(StringExternalizer, LongExternalizer)) { @Synchronized fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) { @@ -577,36 +581,34 @@ open class IncrementalJvmCache( storage.remove(key) } - // Note: If we detect a change in an inline function `foo` with @JvmName `fooJvmName`, we have two options: - // 1. Report that function `foo` has changed - // 2. Report that method `fooJvmName` has changed - // - // Similarly, if we detect a change in an inline property accessor with JvmName `getFoo` of property `foo`, we have two options: - // 1. Report that property `foo` has changed - // 2. Report that property accessor `getFoo` has changed - // - // The compiler is guaranteed to generate `LookupSymbol`s corresponding to option 1 when referencing inline functions/property - // accessors, but it is not guaranteed to generate `LookupSymbol`s corresponding to option 2. (Currently the compiler seems to - // support option 2 for *inline* functions/property accessors, but that may change.) - // - // In the following, we will choose option 1 as it is cleaner and safer. - val scope = kotlinClassInfo.scopeFqName() - (oldMap.keys + newMap.keys).forEach { - val name = when (it) { - is InlineFunction -> it.kotlinFunctionName - is InlinePropertyAccessor -> it.propertyName - } - changesCollector.collectMemberIfValueWasChanged(scope, name, oldMap[it], newMap[it]) + 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( + kotlinClassInfo.scopeFqName(), + inlineFunctionOrAccessorName, + oldMap[methodSignature], + newMap[methodSignature] + ) } } + // TODO get name in better way instead of using substringBefore + private fun functionNameBySignature(signature: String): String = + signature.substringBefore("(") + @Synchronized fun remove(className: JvmClassName) { storage.remove(className.internalName) } - override fun dumpValue(value: Map): String = - value.mapKeys { it.key.jvmMethodSignature.asString() }.dumpMap { java.lang.Long.toHexString(it) } + override fun dumpValue(value: LinkedHashMap): String = + value.dumpMap { java.lang.Long.toHexString(it) } } private fun KotlinClassInfo.scopeFqName() = when (classKind) { @@ -667,3 +669,182 @@ fun , V> Map.dumpMap(dumpValue: (V) -> String): String = @TestOnly fun > Collection.dumpCollection(): String = "[${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 { + 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` + * task (see [IncrementalJvmCache.saveClassToCache]). + * + * It's important that this class contain only the minimal required information, as it will be part of the classpath snapshot of the + * `KotlinCompile` task and the task needs to support compile avoidance. For example, this class should contain public method signatures, + * and should not contain private method signatures, or method implementations. + */ +class KotlinClassInfo constructor( + val classId: ClassId, + val classKind: KotlinClassHeader.Kind, + val classHeaderData: Array, // Can be empty + val classHeaderStrings: Array, // Can be empty + val multifileClassName: String?, // Not null iff classKind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART + val constantsMap: LinkedHashMap, + val inlineFunctionsAndAccessorsMap: LinkedHashMap +) { + + val className: JvmClassName by lazy { JvmClassName.byClassId(classId) } + + val protoMapValue: ProtoMapValue by lazy { + ProtoMapValue( + isPackageFacade = classKind != KotlinClassHeader.Kind.CLASS, + BitEncoding.decodeBytes(classHeaderData), + classHeaderStrings + ) + } + + /** + * 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. + */ + val protoData: ProtoData by lazy { + check(classKind != KotlinClassHeader.Kind.MULTIFILE_CLASS) { + "Proto data is not available for KotlinClassHeader.Kind.MULTIFILE_CLASS: $classId" + } + 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? 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 { + return createFrom(kotlinClass.classId, kotlinClass.classHeader, kotlinClass.fileContents) + } + + fun createFrom(classId: ClassId, classHeader: KotlinClassHeader, classContents: ByteArray): KotlinClassInfo { + val (constants, inlineFunctionsAndAccessors) = getConstantsAndInlineFunctionsOrAccessors(classHeader, classContents) + + return KotlinClassInfo( + classId, + classHeader.kind, + classHeader.data ?: emptyArray(), + classHeader.strings ?: emptyArray(), + classHeader.multifileClassName, + constants.mapKeysTo(LinkedHashMap()) { it.key.name }, + inlineFunctionsAndAccessors.mapKeysTo(LinkedHashMap()) { it.key.asString() }, + ) + } + } +} + +/** + * 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, + classContents: ByteArray +): Pair, Map> { + val constantsClassVisitor = ConstantsClassVisitor() + val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(classHeader) + + return if (inlineFunctionsAndAccessors.isEmpty()) { + // parsingOptions = (SKIP_CODE, SKIP_DEBUG) as method bodies and debug info are not important for constants + ClassReader(classContents).accept(constantsClassVisitor, SKIP_CODE or SKIP_DEBUG) + Pair(constantsClassVisitor.getResult(), emptyMap()) + } else { + val inlineFunctionsAndAccessorsClassVisitor = + InlineFunctionsAndAccessorsClassVisitor(inlineFunctionsAndAccessors.toSet(), constantsClassVisitor) + // 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 val result = mutableMapOf() + + 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 + + val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL + if (value != null && access and staticFinal == staticFinal) { + result[JvmMemberSignature.Field(name, desc)] = value + } + return null + } + + fun getResult() = result +} + +private class InlineFunctionsAndAccessorsClassVisitor( + private val inlineFunctionsAndAccessors: Set, + cv: ConstantsClassVisitor // Note: cv must not override `visitMethod` (it will not be called with the current implementation below) +) : ClassVisitor(Opcodes.API_VERSION, cv) { + + private val result = mutableMapOf() + private var classVersion: Int? = null + + override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array?) { + super.visit(version, access, name, signature, superName, interfaces) + classVersion = version + } + + override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { + if (access and Opcodes.ACC_PRIVATE == Opcodes.ACC_PRIVATE) return null + + val method = JvmMemberSignature.Method(name, desc) + if (method !in inlineFunctionsAndAccessors) return null + + val classWriter = ClassWriter(0) + + // The `version` and `name` parameters are important (see KT-38857), the others can be null. + classWriter.visit(/* version */ classVersion!!, /* access */ 0, /* name */ "ClassWithOneMethod", null, null, null) + + return object : MethodVisitor(Opcodes.API_VERSION, classWriter.visitMethod(access, name, desc, signature, exceptions)) { + override fun visitEnd() { + result[method] = classWriter.toByteArray().md5() + } + } + } + + fun getResult() = result +} diff --git a/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt b/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt deleted file mode 100644 index 57b121791bf..00000000000 --- a/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.incremental - -import com.intellij.util.io.DataExternalizer -import org.jetbrains.kotlin.incremental.storage.* -import org.jetbrains.kotlin.inline.InlineFunctionOrAccessor -import org.jetbrains.kotlin.inline.inlineFunctionsAndAccessors -import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader -import org.jetbrains.kotlin.metadata.jvm.deserialization.BitEncoding -import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.jvm.JvmClassName -import org.jetbrains.org.objectweb.asm.* - -/** - * Minimal information about a Kotlin class to compute recompilation-triggering changes during an incremental run of the `KotlinCompile` - * task (see [IncrementalJvmCache.saveClassToCache]). - * - * It's important that this class contain only the minimal required information, as it will be part of the classpath snapshot of the - * `KotlinCompile` task and the task needs to support compile avoidance. For example, this class should contain public method signatures, - * and should not contain private method signatures, or method implementations. - */ -class KotlinClassInfo constructor( - val classId: ClassId, - val classKind: KotlinClassHeader.Kind, - val classHeaderData: Array, // Can be empty - val classHeaderStrings: Array, // Can be empty - val multifileClassName: String?, // Not null iff classKind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART - val constantsMap: Map, - val inlineFunctionsAndAccessorsMap: Map -) { - - val className: JvmClassName by lazy { JvmClassName.byClassId(classId) } - - val protoMapValue: ProtoMapValue by lazy { - ProtoMapValue( - isPackageFacade = classKind != KotlinClassHeader.Kind.CLASS, - BitEncoding.decodeBytes(classHeaderData), - classHeaderStrings - ) - } - - /** - * 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. - */ - val protoData: ProtoData by lazy { - check(classKind != KotlinClassHeader.Kind.MULTIFILE_CLASS) { - "Proto data is not available for KotlinClassHeader.Kind.MULTIFILE_CLASS: $classId" - } - 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? 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 { - return createFrom(kotlinClass.classId, kotlinClass.classHeader, kotlinClass.fileContents) - } - - fun createFrom(classId: ClassId, classHeader: KotlinClassHeader, classContents: ByteArray): KotlinClassInfo { - val (constants, inlineFunctionsAndAccessors) = getConstantsAndInlineFunctionsOrAccessors(classHeader, classContents) - - return KotlinClassInfo( - classId, - classHeader.kind, - classHeader.data ?: emptyArray(), - classHeader.strings ?: emptyArray(), - classHeader.multifileClassName, - constants.mapKeys { it.key.name }, - inlineFunctionsAndAccessors - ) - } - } -} - -/** - * 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, - classContents: ByteArray -): Pair, Map> { - val constantsClassVisitor = ConstantsClassVisitor() - val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(classHeader, excludePrivateMembers = true) - - return if (inlineFunctionsAndAccessors.isEmpty()) { - // Handle this case differently to improve performance - // parsingOptions = (SKIP_CODE, SKIP_DEBUG) as method bodies and debug info are not important for constants - ClassReader(classContents).accept(constantsClassVisitor, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG) - Pair(constantsClassVisitor.getResult(), emptyMap()) - } else { - val inlineFunctionsAndAccessorsClassVisitor = InlineFunctionsAndAccessorsClassVisitor( - inlineFunctionsAndAccessors.map { it.jvmMethodSignature }.toSet(), - constantsClassVisitor - ) - // 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) - val constantsMap = constantsClassVisitor.getResult() - val methodHashesMap = inlineFunctionsAndAccessorsClassVisitor.getResult() - val inlineFunctionsAndAccessorsMap = inlineFunctionsAndAccessors.mapNotNull { inline -> - // Note that internal/private inline functions may be removed from the bytecode if code shrinker is used. For example, - // `kotlin-reflect-1.7.20.jar` contains `/kotlin/reflect/jvm/internal/UtilKt.class` in which the internal inline function - // `reflectionCall` appears in the Kotlin metadata (also in the source file), but not in the bytecode. - // When that happens (i.e., when the map lookup below returns null), we will ignore the method. It is safe to ignore because the - // method is not declared in the bytecode and therefore can't be referenced. - methodHashesMap[inline.jvmMethodSignature]?.let { inline to it } - }.toMap() - Pair(constantsMap, inlineFunctionsAndAccessorsMap) - } -} - -private class ConstantsClassVisitor : ClassVisitor(Opcodes.API_VERSION) { - private val result = mutableMapOf() - - 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 - - val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL - if (value != null && access and staticFinal == staticFinal) { - result[JvmMemberSignature.Field(name, desc)] = value - } - return null - } - - fun getResult() = result -} - -private class InlineFunctionsAndAccessorsClassVisitor( - private val inlineFunctionsAndAccessors: Set, - cv: ConstantsClassVisitor // Note: cv must not override `visitMethod` (it will not be called with the current implementation below) -) : ClassVisitor(Opcodes.API_VERSION, cv) { - - private val result = mutableMapOf() - private var classVersion: Int? = null - - override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array?) { - super.visit(version, access, name, signature, superName, interfaces) - classVersion = version - } - - override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { - // Note: Do not filter out private methods here because a *public* inline function may actually have a *private* corresponding JVM - // method in the bytecode (see `InlineOnlyKt.isInlineOnlyPrivateInBytecode`). - // Just filter the methods based on the given `inlineFunctionsAndAccessors` set. - val method = JvmMemberSignature.Method(name, desc) - if (method !in inlineFunctionsAndAccessors) return null - - val classWriter = ClassWriter(0) - - // The `version` and `name` parameters are important (see KT-38857), the others can be null. - classWriter.visit(/* version */ classVersion!!, /* access */ 0, /* name */ "ClassWithOneMethod", null, null, null) - - return object : MethodVisitor(Opcodes.API_VERSION, classWriter.visitMethod(access, name, desc, signature, exceptions)) { - override fun visitEnd() { - result[method] = classWriter.toByteArray().md5() - } - } - } - - fun getResult() = result -} - -/** - * [DataExternalizer] for the value of a Kotlin constant. - * - * The constants' values are provided by ASM (see the javadoc of [ConstantsClassVisitor.visitField]), so their types can only be the - * following: Integer, Long, Float, Double, String. (Boolean constants have Integer (0, 1) values in ASM.) - */ -object ConstantValueExternalizer : DataExternalizer by DelegateDataExternalizer( - listOf( - java.lang.Integer::class.java, - java.lang.Long::class.java, - java.lang.Float::class.java, - java.lang.Double::class.java, - java.lang.String::class.java - ), - listOf(IntExternalizer, LongExternalizer, FloatExternalizer, DoubleExternalizer, StringExternalizer) -) diff --git a/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt b/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt index a5d46ff8e87..a5c2fd2d45f 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt @@ -22,10 +22,6 @@ import com.intellij.util.io.DataExternalizer import com.intellij.util.io.EnumeratorStringDescriptor import com.intellij.util.io.IOUtil import com.intellij.util.io.KeyDescriptor -import org.jetbrains.kotlin.inline.InlineFunction -import org.jetbrains.kotlin.inline.InlineFunctionOrAccessor -import org.jetbrains.kotlin.inline.InlinePropertyAccessor -import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.JvmClassName @@ -169,6 +165,53 @@ object StringToLongMapExternalizer : StringMapExternalizer() { } } +/** [DataExternalizer] for a Kotlin constant. */ +object ConstantExternalizer : DataExternalizer { + + override fun save(output: DataOutput, value: Any) { + when (value) { + is Int -> { + output.writeByte(Kind.INT.ordinal) + output.writeInt(value) + } + is Float -> { + output.writeByte(Kind.FLOAT.ordinal) + output.writeFloat(value) + } + is Long -> { + output.writeByte(Kind.LONG.ordinal) + output.writeLong(value) + } + is Double -> { + output.writeByte(Kind.DOUBLE.ordinal) + output.writeDouble(value) + } + is String -> { + output.writeByte(Kind.STRING.ordinal) + output.writeString(value) + } + else -> throw IllegalStateException("Unexpected constant class: ${value::class.java}") + } + } + + override fun read(input: DataInput): Any { + return when (Kind.values()[input.readByte().toInt()]) { + Kind.INT -> input.readInt() + Kind.FLOAT -> input.readFloat() + Kind.LONG -> input.readLong() + Kind.DOUBLE -> input.readDouble() + Kind.STRING -> input.readString() + } + } + + // The constants' values are provided by ASM, so their types can only be the following. + // See https://asm.ow2.io/javadoc/org/objectweb/asm/ClassVisitor.html#visitField(int,java.lang.String,java.lang.String,java.lang.String,java.lang.Object) + // (Note: Boolean constants have Integer (0, 1) values in ASM.) + private enum class Kind { + INT, FLOAT, LONG, DOUBLE, STRING + } +} + fun DataExternalizer.saveToFile(file: File, value: T) { return DataOutputStream(FileOutputStream(file).buffered()).use { save(it, value) @@ -205,16 +248,6 @@ object LongExternalizer : DataExternalizer { override fun read(input: DataInput): Long = input.readLong() } -object FloatExternalizer : DataExternalizer { - override fun save(output: DataOutput, value: Float) = output.writeFloat(value) - override fun read(input: DataInput): Float = input.readFloat() -} - -object DoubleExternalizer : DataExternalizer { - override fun save(output: DataOutput, value: Double) = output.writeDouble(value) - override fun read(input: DataInput): Double = input.readDouble() -} - object StringExternalizer : DataExternalizer { override fun save(output: DataOutput, value: String) = IOUtil.writeString(value, output) override fun read(input: DataInput): String = IOUtil.readString(input) @@ -235,33 +268,6 @@ object PathStringDescriptor : EnumeratorStringDescriptor() { } } -/** [DataExternalizer] that delegates to another [DataExternalizer] depending on the type of the object to externalize. */ -class DelegateDataExternalizer( - val types: List>, - val typesExternalizers: List> -) : DataExternalizer { - - init { - check(types.size == typesExternalizers.size) - check(types.size < Byte.MAX_VALUE) // We will writeByte(index), so we need lastIndex (types.size - 1) <= Byte.MAX_VALUE - } - - override fun save(output: DataOutput, objectToExternalize: T) { - val type = types.single { it.isAssignableFrom(objectToExternalize!!::class.java) } - val typeIndex = types.indexOf(type) - - output.writeByte(typeIndex) - @Suppress("UNCHECKED_CAST") - (typesExternalizers[typeIndex] as DataExternalizer).save(output, objectToExternalize) - } - - override fun read(input: DataInput): T { - val typeIndex = input.readByte().toInt() - @Suppress("UNCHECKED_CAST") - return typesExternalizers[typeIndex].read(input) as T - } -} - /** * [DataExternalizer] for a [Collection]. * @@ -395,53 +401,3 @@ class LinkedHashMapExternalizer( keyExternalizer: DataExternalizer, valueExternalizer: DataExternalizer ) : MapExternalizer>(keyExternalizer, valueExternalizer, { size -> LinkedHashMap(size) }) - -object JvmMethodSignatureExternalizer : DataExternalizer { - - override fun save(output: DataOutput, method: JvmMemberSignature.Method) { - StringExternalizer.save(output, method.name) - StringExternalizer.save(output, method.desc) - } - - override fun read(input: DataInput): JvmMemberSignature.Method { - return JvmMemberSignature.Method( - name = StringExternalizer.read(input), - desc = StringExternalizer.read(input) - ) - } -} - -object InlineFunctionOrAccessorExternalizer : DataExternalizer by DelegateDataExternalizer( - types = listOf(InlineFunction::class.java, InlinePropertyAccessor::class.java), - typesExternalizers = listOf(InlineFunctionExternalizer, InlinePropertyAccessorExternalizer) -) - -private object InlineFunctionExternalizer : DataExternalizer { - - override fun save(output: DataOutput, function: InlineFunction) { - JvmMethodSignatureExternalizer.save(output, function.jvmMethodSignature) - StringExternalizer.save(output, function.kotlinFunctionName) - } - - override fun read(input: DataInput): InlineFunction { - return InlineFunction( - jvmMethodSignature = JvmMethodSignatureExternalizer.read(input), - kotlinFunctionName = StringExternalizer.read(input) - ) - } -} - -private object InlinePropertyAccessorExternalizer : DataExternalizer { - - override fun save(output: DataOutput, accessor: InlinePropertyAccessor) { - JvmMethodSignatureExternalizer.save(output, accessor.jvmMethodSignature) - StringExternalizer.save(output, accessor.propertyName) - } - - override fun read(input: DataInput): InlinePropertyAccessor { - return InlinePropertyAccessor( - jvmMethodSignature = JvmMethodSignatureExternalizer.read(input), - propertyName = StringExternalizer.read(input) - ) - } -} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/inline/inlineUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/inline/inlineUtil.kt index c33c8d4d341..edd4e01c5cc 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/inline/inlineUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/inline/inlineUtil.kt @@ -24,44 +24,27 @@ import org.jetbrains.kotlin.metadata.deserialization.NameResolver import org.jetbrains.kotlin.metadata.deserialization.TypeTable import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf +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.serialization.deserialization.ProtoEnumFlags import org.jetbrains.kotlin.serialization.deserialization.descriptorVisibility -sealed interface InlineFunctionOrAccessor { - val jvmMethodSignature: JvmMemberSignature.Method -} - -data class InlineFunction( - override val jvmMethodSignature: JvmMemberSignature.Method, - - /** The Kotlin name of the function. It may be different from the JVM name of the function if [JvmName] is used. */ - val kotlinFunctionName: String -) : InlineFunctionOrAccessor - -data class InlinePropertyAccessor( - override val jvmMethodSignature: JvmMemberSignature.Method, - - /** The name of the property that this property accessor belongs to. */ - val propertyName: String -) : InlineFunctionOrAccessor - -fun inlineFunctionsAndAccessors(header: KotlinClassHeader, excludePrivateMembers: Boolean = false): List { +fun inlineFunctionsAndAccessors(header: KotlinClassHeader): List { val data = header.data ?: return emptyList() val strings = header.strings ?: return emptyList() return when (header.kind) { KotlinClassHeader.Kind.CLASS -> { val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(data, strings) - inlineFunctions(classProto.functionList, nameResolver, classProto.typeTable, excludePrivateMembers) + - inlinePropertyAccessors(classProto.propertyList, nameResolver, excludePrivateMembers) + inlineFunctions(classProto.functionList, nameResolver, classProto.typeTable) + + inlineAccessors(classProto.propertyList, nameResolver) } KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> { val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings) - inlineFunctions(packageProto.functionList, nameResolver, packageProto.typeTable, excludePrivateMembers) + - inlinePropertyAccessors(packageProto.propertyList, nameResolver, excludePrivateMembers) + inlineFunctions(packageProto.functionList, nameResolver, packageProto.typeTable) + + inlineAccessors(packageProto.propertyList, nameResolver) } else -> emptyList() } @@ -70,51 +53,40 @@ fun inlineFunctionsAndAccessors(header: KotlinClassHeader, excludePrivateMembers private fun inlineFunctions( functions: List, nameResolver: NameResolver, - protoTypeTable: ProtoBuf.TypeTable, - excludePrivateFunctions: Boolean = false -): List { + protoTypeTable: ProtoBuf.TypeTable +): List { val typeTable = TypeTable(protoTypeTable) - return functions - .filter { Flags.IS_INLINE.get(it.flags) && (!excludePrivateFunctions || !isPrivate(it.flags)) } - .mapNotNull { inlineFunction -> - JvmProtoBufUtil.getJvmMethodSignature(inlineFunction, nameResolver, typeTable)?.let { - InlineFunction(jvmMethodSignature = it, kotlinFunctionName = nameResolver.getString(inlineFunction.name)) - } - } + return functions.filter { Flags.IS_INLINE.get(it.flags) }.mapNotNull { + JvmProtoBufUtil.getJvmMethodSignature(it, nameResolver, typeTable) + } } -private fun inlinePropertyAccessors( +fun inlineAccessors( properties: List, nameResolver: NameResolver, excludePrivateAccessors: Boolean = false -): List { - val inlineAccessors = mutableListOf() +): List { + val inlineAccessors = mutableListOf() + + fun isInline(flags: Int) = Flags.IS_INLINE_ACCESSOR.get(flags) + 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() && Flags.IS_INLINE_ACCESSOR.get(property.getterFlags) - && (!excludePrivateAccessors || !isPrivate(property.getterFlags)) - ) { - val getter = propertySignature.getter - inlineAccessors.add( - InlinePropertyAccessor( - JvmMemberSignature.Method(name = nameResolver.getString(getter.name), desc = nameResolver.getString(getter.desc)), - propertyName = nameResolver.getString(property.name) - ) - ) + + if (property.hasGetterFlags() && isInline(property.getterFlags)) { + if (!(excludePrivateAccessors && isPrivate(property.getterFlags))) { + inlineAccessors.add(propertySignature.getter) + } } - if (property.hasSetterFlags() && Flags.IS_INLINE_ACCESSOR.get(property.setterFlags) - && (!excludePrivateAccessors || !isPrivate(property.setterFlags)) - ) { - val setter = propertySignature.setter - inlineAccessors.add( - InlinePropertyAccessor( - JvmMemberSignature.Method(name = nameResolver.getString(setter.name), desc = nameResolver.getString(setter.desc)), - propertyName = nameResolver.getString(property.name) - ) - ) + if (property.hasSetterFlags() && isInline(property.setterFlags)) { + if (!(excludePrivateAccessors && isPrivate(property.setterFlags))) { + inlineAccessors.add(propertySignature.setter) + } } } - return inlineAccessors -} -private fun isPrivate(flags: Int) = DescriptorVisibilities.isPrivate(ProtoEnumFlags.descriptorVisibility(Flags.VISIBILITY.get(flags))) + return inlineAccessors.map { + JvmMemberSignature.Method(name = nameResolver.getString(it.name), desc = nameResolver.getString(it.desc)) + } +} diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/AbiSnapshotDiffService.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/AbiSnapshotDiffService.kt index c5a7452d8a9..59ed70b99bc 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/AbiSnapshotDiffService.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/AbiSnapshotDiffService.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.incremental -import org.jetbrains.kotlin.incremental.DifferenceCalculatorForClass.Companion.getNonPrivateMembers import org.jetbrains.kotlin.metadata.ProtoBuf.Visibility.PRIVATE import org.jetbrains.kotlin.metadata.deserialization.Flags import org.jetbrains.kotlin.name.FqName @@ -98,7 +97,8 @@ class AbiSnapshotDiffService() { when (protoData) { is ClassProtoData -> { fqNames.add(fqName) - symbols.addAll(protoData.getNonPrivateMembers().map { LookupSymbol(it, fqName.asString()) }) + symbols.addAll( + protoData.getNonPrivateMemberNames(includeInlineAccessors = true).map { LookupSymbol(it, fqName.asString()) }) } is PackagePartProtoData -> { symbols.addAll( diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt index b9cc910bee0..3f9593bee7f 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalCompilerRunner.kt @@ -18,15 +18,12 @@ package org.jetbrains.kotlin.incremental import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS import org.jetbrains.kotlin.build.GeneratedFile -import org.jetbrains.kotlin.build.report.BuildReporter -import org.jetbrains.kotlin.build.report.debug -import org.jetbrains.kotlin.build.report.info +import org.jetbrains.kotlin.build.report.* import org.jetbrains.kotlin.build.report.metrics.BuildAttribute import org.jetbrains.kotlin.build.report.metrics.BuildAttribute.* import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric import org.jetbrains.kotlin.build.report.metrics.BuildTime import org.jetbrains.kotlin.build.report.metrics.measure -import org.jetbrains.kotlin.build.report.warn import org.jetbrains.kotlin.cli.common.* import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -83,12 +80,12 @@ abstract class IncrementalCompilerRunner< allSourceFiles: List, args: Args, messageCollector: MessageCollector, - // when `changedFiles` is not null, changes are provided by external system (e.g. Gradle) + // when [providedChangedFiles] is not null, changes are provided by external system (e.g. Gradle) // otherwise we track source files changes ourselves. - changedFiles: ChangedFiles?, + providedChangedFiles: ChangedFiles?, projectDir: File? = null ): ExitCode = reporter.measure(BuildTime.INCREMENTAL_COMPILATION_DAEMON) { - return when (val result = tryCompileIncrementally(allSourceFiles, changedFiles, args, projectDir, messageCollector)) { + return when (val result = tryCompileIncrementally(allSourceFiles, providedChangedFiles, args, projectDir, messageCollector)) { is ICResult.Completed -> { reporter.debug { "Incremental compilation completed" } result.exitCode @@ -98,7 +95,7 @@ abstract class IncrementalCompilerRunner< reporter.addAttribute(result.reason) compileNonIncrementally( - result.reason, allSourceFiles, args, projectDir, trackChangedFiles = changedFiles == null, messageCollector + result.reason, allSourceFiles, args, projectDir, trackChangedFiles = providedChangedFiles == null, messageCollector ) } is ICResult.Failed -> { @@ -117,7 +114,7 @@ abstract class IncrementalCompilerRunner< reporter.addAttribute(result.reason) compileNonIncrementally( - result.reason, allSourceFiles, args, projectDir, trackChangedFiles = changedFiles == null, messageCollector + result.reason, allSourceFiles, args, projectDir, trackChangedFiles = providedChangedFiles == null, messageCollector ) } } @@ -144,24 +141,23 @@ abstract class IncrementalCompilerRunner< */ private fun tryCompileIncrementally( allSourceFiles: List, - changedFiles: ChangedFiles?, + providedChangedFiles: ChangedFiles?, args: Args, projectDir: File?, messageCollector: MessageCollector ): ICResult { - if (changedFiles is ChangedFiles.Unknown) { + if (providedChangedFiles is ChangedFiles.Unknown) { return ICResult.RequiresRebuild(UNKNOWN_CHANGES_IN_GRADLE_INPUTS) } - changedFiles as ChangedFiles.Known? + providedChangedFiles as ChangedFiles.Known? val caches = createCacheManager(args, projectDir) val exitCode: ExitCode try { // Step 1: Get changed files - val knownChangedFiles: ChangedFiles.Known = try { - getChangedFiles(changedFiles, allSourceFiles, caches) + val changedFiles: ChangedFiles.Known = try { + getChangedFiles(providedChangedFiles, allSourceFiles, caches) } catch (e: Throwable) { - // Don't need to close caches in cases where we return `ICResult.Failed` because we will compile non-incrementally anyway return ICResult.Failed(IC_FAILED_TO_GET_CHANGED_FILES, e) } @@ -170,7 +166,7 @@ abstract class IncrementalCompilerRunner< // Step 2: Compute files to recompile val compilationMode = try { reporter.measure(BuildTime.IC_CALCULATE_INITIAL_DIRTY_SET) { - calculateSourcesToCompile(caches, knownChangedFiles, args, messageCollector, classpathAbiSnapshot ?: emptyMap()) + calculateSourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot ?: emptyMap()) } } catch (e: Throwable) { return ICResult.Failed(IC_FAILED_TO_COMPUTE_FILES_TO_RECOMPILE, e) @@ -231,7 +227,7 @@ abstract class IncrementalCompilerRunner< check(it.containsAll(mainOutputDirs)) { "outputDirs is missing classesDir and workingDir: $it" } } ?: mainOutputDirs - reporter.debug { "Cleaning ${outputDirsToClean.size} output directories" } + reporter.debug { "Cleaning output directories" } cleanOrCreateDirectories(outputDirsToClean) } return createCacheManager(args, projectDir).use { caches -> @@ -270,20 +266,20 @@ abstract class IncrementalCompilerRunner< } private fun getChangedFiles( - changedFiles: ChangedFiles.Known?, + providedChangedFiles: ChangedFiles.Known?, allSourceFiles: List, caches: CacheManager ): ChangedFiles.Known { return when { - changedFiles == null -> caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) - changedFiles.forDependencies -> { + providedChangedFiles == null -> caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) + providedChangedFiles.forDependencies -> { val moreChangedFiles = caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) ChangedFiles.Known( - modified = changedFiles.modified + moreChangedFiles.modified, - removed = changedFiles.removed + moreChangedFiles.removed + modified = providedChangedFiles.modified + moreChangedFiles.modified, + removed = providedChangedFiles.removed + moreChangedFiles.removed ) } - else -> changedFiles + else -> providedChangedFiles } } diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt index 822396f93d2..2f27915a9c4 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunner.kt @@ -109,7 +109,7 @@ fun makeIncrementally( classpathChanges = ClasspathSnapshotDisabled ) //TODO set properly - compiler.compile(sourceFiles, args, messageCollector, changedFiles = null) + compiler.compile(sourceFiles, args, messageCollector, providedChangedFiles = null) } } @@ -388,7 +388,9 @@ open class IncrementalJvmCompilerRunner( super.performWorkBeforeCompilation(compilationMode, args) if (compilationMode is CompilationMode.Incremental) { - args.classpathAsList = listOf(args.destinationAsFile) + args.classpathAsList + val destinationDir = args.destinationAsFile + destinationDir.mkdirs() + args.classpathAsList = listOf(destinationDir) + args.classpathAsList } } diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotSerializer.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotSerializer.kt index d7d399cbfa1..f41bfbb8e21 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotSerializer.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotSerializer.kt @@ -8,7 +8,6 @@ package org.jetbrains.kotlin.incremental.classpathDiff import com.intellij.util.containers.Interner import com.intellij.util.io.DataExternalizer import org.jetbrains.kotlin.build.report.metrics.BuildPerformanceMetric -import org.jetbrains.kotlin.incremental.ConstantValueExternalizer import org.jetbrains.kotlin.incremental.KotlinClassInfo import org.jetbrains.kotlin.incremental.storage.* import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader @@ -54,6 +53,27 @@ object CachedClasspathSnapshotSerializer { } } +internal open class DataExternalizerForSealedClass( + val baseClass: Class, + val inheritorClasses: List>, + val inheritorExternalizers: List> +) : DataExternalizer { + + override fun save(output: DataOutput, objectToExternalize: T) { + val inheritorClassIndex = + inheritorClasses.indexOfFirst { it.isAssignableFrom(objectToExternalize!!::class.java) }.also { check(it != -1) } + output.writeByte(inheritorClassIndex.also { check(it <= Byte.MAX_VALUE) }) // Write byte so the data is smaller + @Suppress("UNCHECKED_CAST") + (inheritorExternalizers[inheritorClassIndex] as DataExternalizer).save(output, objectToExternalize) + } + + override fun read(input: DataInput): T { + val inheritorClassIndex = input.readByte().toInt() + @Suppress("UNCHECKED_CAST") + return inheritorExternalizers[inheritorClassIndex].read(input) as T + } +} + object ClasspathEntrySnapshotExternalizer : DataExternalizer { override fun save(output: DataOutput, snapshot: ClasspathEntrySnapshot) { @@ -67,23 +87,26 @@ object ClasspathEntrySnapshotExternalizer : DataExternalizer by DelegateDataExternalizer( - types = listOf(AccessibleClassSnapshot::class.java, InaccessibleClassSnapshot::class.java), - typesExternalizers = listOf(AccessibleClassSnapshotExternalizer, InaccessibleClassSnapshotExternalizer) +internal object ClassSnapshotExternalizer : DataExternalizerForSealedClass( + baseClass = ClassSnapshot::class.java, + inheritorClasses = listOf(AccessibleClassSnapshot::class.java, InaccessibleClassSnapshot::class.java), + inheritorExternalizers = listOf(AccessibleClassSnapshotExternalizer, InaccessibleClassSnapshotExternalizer) ) -internal object AccessibleClassSnapshotExternalizer : DataExternalizer by DelegateDataExternalizer( - types = listOf(KotlinClassSnapshot::class.java, JavaClassSnapshot::class.java), - typesExternalizers = listOf(KotlinClassSnapshotExternalizer, JavaClassSnapshotExternalizer) +internal object AccessibleClassSnapshotExternalizer : DataExternalizerForSealedClass( + baseClass = AccessibleClassSnapshot::class.java, + inheritorClasses = listOf(KotlinClassSnapshot::class.java, JavaClassSnapshot::class.java), + inheritorExternalizers = listOf(KotlinClassSnapshotExternalizer, JavaClassSnapshotExternalizer) ) -private object KotlinClassSnapshotExternalizer : DataExternalizer by DelegateDataExternalizer( - types = listOf( +private object KotlinClassSnapshotExternalizer : DataExternalizerForSealedClass( + baseClass = KotlinClassSnapshot::class.java, + inheritorClasses = listOf( RegularKotlinClassSnapshot::class.java, PackageFacadeKotlinClassSnapshot::class.java, MultifileClassKotlinClassSnapshot::class.java ), - typesExternalizers = listOf( + inheritorExternalizers = listOf( RegularKotlinClassSnapshotExternalizer, PackageFacadeKotlinClassSnapshotExternalizer, MultifileClassKotlinClassSnapshotExternalizer @@ -162,8 +185,8 @@ internal object KotlinClassInfoExternalizer : DataExternalizer ListExternalizer(StringExternalizer).save(output, info.classHeaderData.toList()) ListExternalizer(StringExternalizer).save(output, info.classHeaderStrings.toList()) NullableValueExternalizer(StringExternalizer).save(output, info.multifileClassName) - MapExternalizer(StringExternalizer, ConstantValueExternalizer).save(output, info.constantsMap) - MapExternalizer(InlineFunctionOrAccessorExternalizer, LongExternalizer).save(output, info.inlineFunctionsAndAccessorsMap) + LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).save(output, info.constantsMap) + LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).save(output, info.inlineFunctionsAndAccessorsMap) } override fun read(input: DataInput): KotlinClassInfo { @@ -174,8 +197,8 @@ internal object KotlinClassInfoExternalizer : DataExternalizer classHeaderData = ListExternalizer(StringExternalizer).read(input).toTypedArray(), classHeaderStrings = ListExternalizer(StringExternalizer).read(input).toTypedArray(), multifileClassName = NullableValueExternalizer(StringExternalizer).read(input), - constantsMap = MapExternalizer(StringExternalizer, ConstantValueExternalizer).read(input), - inlineFunctionsAndAccessorsMap = MapExternalizer(InlineFunctionOrAccessorExternalizer, LongExternalizer).read(input) + constantsMap = LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).read(input), + inlineFunctionsAndAccessorsMap = LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).read(input) ) } } diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotter.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotter.kt index 443ece74095..90e1a2d5f28 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotter.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotter.kt @@ -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.DoNothingBuildMetricsReporter import org.jetbrains.kotlin.build.report.metrics.measure -import org.jetbrains.kotlin.incremental.DifferenceCalculatorForPackageFacade.Companion.getNonPrivateMembers 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.getNonPrivateMemberNames import org.jetbrains.kotlin.incremental.md5 import org.jetbrains.kotlin.incremental.storage.toByteArray import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.* @@ -97,7 +96,7 @@ object ClassSnapshotter { ) FILE_FACADE, MULTIFILE_CLASS_PART -> PackageFacadeKotlinClassSnapshot( classId, classAbiHash, classMemberLevelSnapshot, - packageMemberNames = (kotlinClassInfo.protoData as PackagePartProtoData).getNonPrivateMembers().toSet() + packageMemberNames = kotlinClassInfo.protoData.getNonPrivateMemberNames(includeInlineAccessors = true).toSet() ) MULTIFILE_CLASS -> MultifileClassKotlinClassSnapshot( classId, classAbiHash, classMemberLevelSnapshot, diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest.kt index 72d5c064130..61cf45b6358 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest.kt @@ -223,9 +223,11 @@ class KotlinOnlyClasspathChangesComputerTest : ClasspathChangesComputerTest() { 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 = "inlineProperty_ChangedGetterImpl", scope = "com.example"), - LookupSymbol(name = "inlineProperty_ChangedSetterImpl", 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") ), @@ -236,26 +238,6 @@ class KotlinOnlyClasspathChangesComputerTest : ClasspathChangesComputerTest() { ).assertEquals(changes) } - @Test - fun testFunctionsAndPropertyAccessorsWithJvmNames() { - val changes = computeClasspathChanges(File(testDataDir, "KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/src"), tmpDir) - Changes( - lookupSymbols = setOf( - LookupSymbol(name = "changedFunction", scope = "com.example.SomeClass"), - LookupSymbol(name = "changedPropertyAccessor", scope = "com.example.SomeClass"), - - LookupSymbol(name = "changedInlineFunction", scope = "com.example"), - LookupSymbol(name = "changedInlinePropertyAccessor", scope = "com.example"), - - LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass"), - ), - fqNames = setOf( - "com.example", - "com.example.SomeClass" - ) - ).assertEquals(changes) - } - /** Tests [SupertypesInheritorsImpact]. */ @Test override fun testImpactComputation_SupertypesInheritors() { diff --git a/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClass.class b/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClass.class deleted file mode 100644 index 89cf8b70a4e49b86e2c4458ec0938419187fad2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcma)3O>fgc5PiFL630!6Q_?0)p?ph#5Kb!&NFbzADUcFc)S_H++Bhq^i9af@6O~(j z3wI8jkT{@-BR>i;8$wm_A+Y3`H?#BJ&dz-O_USW#2e`$s8YGb~PepVRO8+Q{_^hPB|B zwB(>Qj)P1k@!llb7m=JR?J-n4qa+Je><=fAzc?e79nWhv7vxuIaw600?M@I#t&{Xj zr1F#gQf6WxGC^D(O)PpSGsZCn3~WUCQ)Qwip}`izi`lg5@;Z09H=DZLF`!7^uFt01 zyvd&CzP!tGkMAveg$8fh&kH7YoWqJ|nfT!|it`QI$$5Drxy{T)wZC-CQ2u2Q45hQl z-ydc4=G|mKb18RJEceGzU#71`Kcvh`CkaH@6{#}uBDrxmjx!a>c05s9QGO?mlT4Tq z(>~mzWSj=Fr3`<4QQG~j5L@81NAp?KJgt@N6^eW_GxE8+AK`o;z;TtVIi@78ku5E7 z5iUg)EK!Ef$@j>cI$nHl>^Y`$j>++Zd7WyOsD7FL3z}dB<^PZ>{adO`ci)(Iu!)=G yTXcg*-B)_B+E{7hVjESg62w{?HPm}x8tZ8EP}FcWHZ<}Ym*$|XQPQ|f4Sokxm!a(d diff --git a/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClassKt.class b/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClassKt.class deleted file mode 100644 index aa7e950a5f2e4e708f23e0bbd1a10ad63ab0d6c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 812 zcma)4&rj4q6#iyf)^2x&vM%B-pn`jl3k74M3GqNkh{Y9+5H5R~ZAa~H+aaB9gEwyc zAKv{3JQ#vE|0v^|%|f!G#>2cH-~0N#*EjRy=htrlp5hV1VyIGIo{ID&k^Z4dIepBwR-1)1)Uduv6 zq7XC{PfLpy!i;f@0RtPFfK%}_HqjkDazG;8S2;9voS1amAfjUb^W>6u&LfHm+njN;aUHvky-X~ijjKjkNitFQ;qY9U@hjeO$&?|$Z zNvK8X|A;lG#Oi+%t5MHY(&t#l8u=I0<2JRJ?ZFB#7r+ZJk4AtyXa-oo-5!|6BJTB2 P(P(M3HEP<#OElRp)6cfR diff --git a/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/previous-classpath/com/example/SomeClass.class b/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/previous-classpath/com/example/SomeClass.class deleted file mode 100644 index 8e631dc4575f4691f59602fd6fd79e411bc7bbc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 841 zcma)3O>fgc5PiFL9M?^WQ_?1Fp?ph#5Kb!&NFbzADc}-X)S_H++Bhrf#2=N{iOMa% zg)0s*s1~#JnX{cF?&|r(<#cW!4c#}KaolPBXYhcrAHfPgq z-eONnU*1{V<$FtRd7Zb+=Vi@p`>^I3nm>F-+*&v7l51(pZKe-u{H0@t>Mw&}sGLpy z{wSwA@1_HqOSKawa(^86W%gS1Bg!my(m+IAk%gKU$&JHtl83SMlS!yT%I_penhQN* zdJpF)9cO`Thg!c@ly-kB#1=Tcqy7wPo}QKB6=I&A8Lg$eA7OtWz;TtL-lin3QLGer z0S>Vm7AeE$;(LXd8}Di7ywX0WDvxYdsh~mVI3GQO62Ftomr6*#lEp!+H-33XZ~tf~9b24ypR6Fg*CjPfjyCn7seWpI#ZayJ!P zzbbElG57;fi69l~L-3|QkP)F4!*cYI5E+LmO_balsi;hH^|H$LMJ5@nN9!R&vpdX7 zst1EA3nsN~23w7LRy#@Oedpgij98{y-WK_>EXq$iQ6#m_i>s0A*XjP+JAb*@Ygvj| zl!CV6S!IzR%ox`gFtDKsI2BJ46KzryZ!tVQKlMC1+;=_h+U;52Zu3p{;^NG``0Q|Z zX073$pBm`d_cKnA)3#milWshI%&;=`)4y-Y%;bfsmP>sZ>Q~pZF)T^S-8?3_=el$W z`=hKci$l>*32AimNTf%iNK8Hfm)?z3nPf6lRiYEZca+LYVQx+{cnA5Yh~!>kzHd!h zkFGUh3mmr5hkOe&aLBJvwqXP#Z(G~emvQwTS%)wVANMJ)k7JIT@F;sgr$z|9GPs(E zT9p0|S#yf4{wJ~;&0HmZj%BQoe@-)Q(TLd|tPpb{{1EeKgt(1nhy~o~foUw_ZV!$| OOQWq((() for (file in files) { val binaryClass = loadBinaryClass(file) - val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader).map { it.jvmMethodSignature }.toSet() + val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader) val classVisitor = object : ClassVisitorWithName() { override fun visitMethod( @@ -81,7 +81,7 @@ object InlineTestUtil { var doLambdaInliningCheck = true for (file in files) { val binaryClass = loadBinaryClass(file) - val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader).map { it.jvmMethodSignature }.toSet() + val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader) //if inline function creates anonymous object then do not try to check that all lambdas are inlined val classVisitor = object : ClassVisitorWithName() { diff --git a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt index e0270f1a8bf..f034cb08d98 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt @@ -67,6 +67,7 @@ import org.junit.Test import java.io.File import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.createTempDirectory +import kotlin.io.path.createTempFile import org.jetbrains.kotlin.konan.file.File as KonanFile @OptIn(ExperimentalPathApi::class) @@ -324,7 +325,7 @@ class GenerateIrRuntime { buildHistoryFile = buildHistoryFile, modulesApiHistory = EmptyModulesApiHistory ) - compiler.compile(allFiles, args, MessageCollector.NONE, changedFiles = null) + compiler.compile(allFiles, args, MessageCollector.NONE, providedChangedFiles = null) } val cleanBuildTime = System.nanoTime() - cleanBuildStart