From cdbbead157df9e14cf8bf99f4d32cc123c5ab704 Mon Sep 17 00:00:00 2001 From: Hung Nguyen Date: Thu, 13 Oct 2022 19:32:14 +0100 Subject: [PATCH] Handle changes to inline functions/property accessors with `@JvmName`s 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.) Therefore, we will choose option 1 as it is cleaner and safer. ^KT-54144 In progress Small cleanup in IncrementalCompilerRunner - Add comment for closing caches - Rename providedChangedFiles to changedFiles - Tiny clean up in `performWorkBeforeCompilation` - Count directories to delete in debug logs ^KT-53015 In progress Extract KotlinClassInfo to a separate class to reduce the size of IncrementalJvmCache and prepare for the next change. ^KT-54144 In progress Ignore inline functions that are not found in the bytecode ^KT-54144 In progress Add unit test for handling `@JvmName`s Test: KotlinOnlyClasspathChangesComputerTest #testFunctionsAndPropertyAccessorsWithJvmNames ^KT-54144 Fixed Update unit tests for handling `@JvmName`s In a previous commit, we made a behavior change for inline property accessors: The existing behavior is that if the implementation of an inline getter has changed, only usages of the getter will be impacted but not usages of the setter (and vice versa). After that previous commit, usages of *both* the getter and setter will now be impacted (i.e., we might compile slightly more files). This is because a change to either the getter or the setter will now be considered a change to the property, which will help simplify our change analysis. This commit updates the relevant unit tests to reflect the new behavior. Test: Updated Incremental*TestGenerated.PureKotlin#testInlinePropertyInClass and Incremental*TestGenerated.PureKotlin#testInlinePropertyOnTopLevel ^KT-54144 Fixed --- .../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 0 -> 841 bytes .../com/example/SomeClassKt.class | Bin 0 -> 812 bytes .../com/example/SomeClass.class | Bin 0 -> 841 bytes .../com/example/SomeClassKt.class | Bin 0 -> 812 bytes .../com/example/SomeClass.kt | 20 ++ .../com/example/SomeClass.kt | 20 ++ .../kotlin/codegen/InlineTestUtil.kt | 4 +- .../inlinePropertyInClass/build.log | 8 +- .../inlinePropertyOnTopLevel/build.log | 8 +- .../kotlin/benchmarks/GenerateIrRuntime.kt | 2 +- 21 files changed, 515 insertions(+), 366 deletions(-) create mode 100644 build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt create mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClass.class create mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/current-classpath/com/example/SomeClassKt.class create mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/previous-classpath/com/example/SomeClass.class create mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/classes/previous-classpath/com/example/SomeClassKt.class create mode 100644 compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputerTest/KotlinOnly/testFunctionsAndPropertyAccessorsWithJvmNames/src/current-classpath/com/example/SomeClass.kt create 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 d28e0ce336b..9603244c671 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/ChangesCollector.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/ChangesCollector.kt @@ -16,6 +16,8 @@ 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 @@ -166,7 +168,7 @@ class ChangesCollector { } private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) { - val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true) + val memberNames = getNonPrivateMembers() if (isRemoved) { collectRemovedMembers(packageFqName, memberNames) } else { @@ -178,14 +180,14 @@ class ChangesCollector { val classFqName = nameResolver.getClassId(proto.fqName).asSingleFqName() if (proto.isCompanionObject) { - val memberNames = getNonPrivateMemberNames(includeInlineAccessors = true) + val memberNames = getNonPrivateMembers() 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 = getNonPrivateMemberNames(includeInlineAccessors = true) + val memberNames = getNonPrivateMembers() 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 41248f93b7e..7341a97bc60 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt @@ -23,27 +23,20 @@ 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.inlineAccessors -import org.jetbrains.kotlin.inline.inlineFunctionsAndAccessors +import org.jetbrains.kotlin.inline.InlineFunction +import org.jetbrains.kotlin.inline.InlineFunctionOrAccessor +import org.jetbrains.kotlin.inline.InlinePropertyAccessor 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 @@ -439,7 +432,7 @@ open class IncrementalJvmCache( // todo: reuse code with InlineFunctionsMap? private inner class ConstantsMap(storageFile: File) : - BasicStringMap>(storageFile, LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer)) { + BasicStringMap>(storageFile, MapExternalizer(StringExternalizer, ConstantValueExternalizer)) { operator fun contains(className: JvmClassName): Boolean = className.internalName in storage @@ -487,7 +480,7 @@ open class IncrementalJvmCache( storage.remove(className.internalName) } - override fun dumpValue(value: LinkedHashMap): String = + override fun dumpValue(value: Map): String = value.dumpMap(Any::toString) } @@ -567,7 +560,10 @@ open class IncrementalJvmCache( } private inner class InlineFunctionsMap(storageFile: File) : - BasicStringMap>(storageFile, LinkedHashMapExternalizer(StringExternalizer, LongExternalizer)) { + BasicStringMap>( + storageFile, + MapExternalizer(InlineFunctionOrAccessorExternalizer, LongExternalizer) + ) { @Synchronized fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) { @@ -581,34 +577,36 @@ open class IncrementalJvmCache( storage.remove(key) } - 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] - ) + // 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]) } } - // 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: LinkedHashMap): String = - value.dumpMap { java.lang.Long.toHexString(it) } + override fun dumpValue(value: Map): String = + value.mapKeys { it.key.jvmMethodSignature.asString() }.dumpMap { java.lang.Long.toHexString(it) } } private fun KotlinClassInfo.scopeFqName() = when (classKind) { @@ -669,182 +667,3 @@ 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 new file mode 100644 index 00000000000..57b121791bf --- /dev/null +++ b/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt @@ -0,0 +1,206 @@ +/* + * 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 a5c2fd2d45f..a5d46ff8e87 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt @@ -22,6 +22,10 @@ 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 @@ -165,53 +169,6 @@ 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) @@ -248,6 +205,16 @@ 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) @@ -268,6 +235,33 @@ 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]. * @@ -401,3 +395,53 @@ 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 edd4e01c5cc..c33c8d4d341 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/inline/inlineUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/inline/inlineUtil.kt @@ -24,27 +24,44 @@ 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 -fun inlineFunctionsAndAccessors(header: KotlinClassHeader): List { +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 { 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) + - inlineAccessors(classProto.propertyList, nameResolver) + inlineFunctions(classProto.functionList, nameResolver, classProto.typeTable, excludePrivateMembers) + + inlinePropertyAccessors(classProto.propertyList, nameResolver, excludePrivateMembers) } KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> { val (nameResolver, packageProto) = JvmProtoBufUtil.readPackageDataFrom(data, strings) - inlineFunctions(packageProto.functionList, nameResolver, packageProto.typeTable) + - inlineAccessors(packageProto.propertyList, nameResolver) + inlineFunctions(packageProto.functionList, nameResolver, packageProto.typeTable, excludePrivateMembers) + + inlinePropertyAccessors(packageProto.propertyList, nameResolver, excludePrivateMembers) } else -> emptyList() } @@ -53,40 +70,51 @@ fun inlineFunctionsAndAccessors(header: KotlinClassHeader): List, nameResolver: NameResolver, - protoTypeTable: ProtoBuf.TypeTable -): List { + protoTypeTable: ProtoBuf.TypeTable, + excludePrivateFunctions: Boolean = false +): List { val typeTable = TypeTable(protoTypeTable) - return functions.filter { Flags.IS_INLINE.get(it.flags) }.mapNotNull { - JvmProtoBufUtil.getJvmMethodSignature(it, nameResolver, typeTable) - } + 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)) + } + } } -fun inlineAccessors( +private fun inlinePropertyAccessors( properties: List, nameResolver: NameResolver, excludePrivateAccessors: Boolean = false -): 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))) - +): List { + val inlineAccessors = mutableListOf() 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.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.hasSetterFlags() && isInline(property.setterFlags)) { - if (!(excludePrivateAccessors && isPrivate(property.setterFlags))) { - inlineAccessors.add(propertySignature.setter) - } + 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) + ) + ) } } - - return inlineAccessors.map { - JvmMemberSignature.Method(name = nameResolver.getString(it.name), desc = nameResolver.getString(it.desc)) - } + return inlineAccessors } + +private fun isPrivate(flags: Int) = DescriptorVisibilities.isPrivate(ProtoEnumFlags.descriptorVisibility(Flags.VISIBILITY.get(flags))) 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 59ed70b99bc..c5a7452d8a9 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,6 +5,7 @@ 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 @@ -97,8 +98,7 @@ class AbiSnapshotDiffService() { when (protoData) { is ClassProtoData -> { fqNames.add(fqName) - symbols.addAll( - protoData.getNonPrivateMemberNames(includeInlineAccessors = true).map { LookupSymbol(it, fqName.asString()) }) + symbols.addAll(protoData.getNonPrivateMembers().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 3f9593bee7f..b9cc910bee0 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,12 +18,15 @@ 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.* +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.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 @@ -80,12 +83,12 @@ abstract class IncrementalCompilerRunner< allSourceFiles: List, args: Args, messageCollector: MessageCollector, - // when [providedChangedFiles] is not null, changes are provided by external system (e.g. Gradle) + // when `changedFiles` is not null, changes are provided by external system (e.g. Gradle) // otherwise we track source files changes ourselves. - providedChangedFiles: ChangedFiles?, + changedFiles: ChangedFiles?, projectDir: File? = null ): ExitCode = reporter.measure(BuildTime.INCREMENTAL_COMPILATION_DAEMON) { - return when (val result = tryCompileIncrementally(allSourceFiles, providedChangedFiles, args, projectDir, messageCollector)) { + return when (val result = tryCompileIncrementally(allSourceFiles, changedFiles, args, projectDir, messageCollector)) { is ICResult.Completed -> { reporter.debug { "Incremental compilation completed" } result.exitCode @@ -95,7 +98,7 @@ abstract class IncrementalCompilerRunner< reporter.addAttribute(result.reason) compileNonIncrementally( - result.reason, allSourceFiles, args, projectDir, trackChangedFiles = providedChangedFiles == null, messageCollector + result.reason, allSourceFiles, args, projectDir, trackChangedFiles = changedFiles == null, messageCollector ) } is ICResult.Failed -> { @@ -114,7 +117,7 @@ abstract class IncrementalCompilerRunner< reporter.addAttribute(result.reason) compileNonIncrementally( - result.reason, allSourceFiles, args, projectDir, trackChangedFiles = providedChangedFiles == null, messageCollector + result.reason, allSourceFiles, args, projectDir, trackChangedFiles = changedFiles == null, messageCollector ) } } @@ -141,23 +144,24 @@ abstract class IncrementalCompilerRunner< */ private fun tryCompileIncrementally( allSourceFiles: List, - providedChangedFiles: ChangedFiles?, + changedFiles: ChangedFiles?, args: Args, projectDir: File?, messageCollector: MessageCollector ): ICResult { - if (providedChangedFiles is ChangedFiles.Unknown) { + if (changedFiles is ChangedFiles.Unknown) { return ICResult.RequiresRebuild(UNKNOWN_CHANGES_IN_GRADLE_INPUTS) } - providedChangedFiles as ChangedFiles.Known? + changedFiles as ChangedFiles.Known? val caches = createCacheManager(args, projectDir) val exitCode: ExitCode try { // Step 1: Get changed files - val changedFiles: ChangedFiles.Known = try { - getChangedFiles(providedChangedFiles, allSourceFiles, caches) + val knownChangedFiles: ChangedFiles.Known = try { + getChangedFiles(changedFiles, 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) } @@ -166,7 +170,7 @@ abstract class IncrementalCompilerRunner< // Step 2: Compute files to recompile val compilationMode = try { reporter.measure(BuildTime.IC_CALCULATE_INITIAL_DIRTY_SET) { - calculateSourcesToCompile(caches, changedFiles, args, messageCollector, classpathAbiSnapshot ?: emptyMap()) + calculateSourcesToCompile(caches, knownChangedFiles, args, messageCollector, classpathAbiSnapshot ?: emptyMap()) } } catch (e: Throwable) { return ICResult.Failed(IC_FAILED_TO_COMPUTE_FILES_TO_RECOMPILE, e) @@ -227,7 +231,7 @@ abstract class IncrementalCompilerRunner< check(it.containsAll(mainOutputDirs)) { "outputDirs is missing classesDir and workingDir: $it" } } ?: mainOutputDirs - reporter.debug { "Cleaning output directories" } + reporter.debug { "Cleaning ${outputDirsToClean.size} output directories" } cleanOrCreateDirectories(outputDirsToClean) } return createCacheManager(args, projectDir).use { caches -> @@ -266,20 +270,20 @@ abstract class IncrementalCompilerRunner< } private fun getChangedFiles( - providedChangedFiles: ChangedFiles.Known?, + changedFiles: ChangedFiles.Known?, allSourceFiles: List, caches: CacheManager ): ChangedFiles.Known { return when { - providedChangedFiles == null -> caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) - providedChangedFiles.forDependencies -> { + changedFiles == null -> caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) + changedFiles.forDependencies -> { val moreChangedFiles = caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles) ChangedFiles.Known( - modified = providedChangedFiles.modified + moreChangedFiles.modified, - removed = providedChangedFiles.removed + moreChangedFiles.removed + modified = changedFiles.modified + moreChangedFiles.modified, + removed = changedFiles.removed + moreChangedFiles.removed ) } - else -> providedChangedFiles + else -> changedFiles } } 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 2f27915a9c4..822396f93d2 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, providedChangedFiles = null) + compiler.compile(sourceFiles, args, messageCollector, changedFiles = null) } } @@ -388,9 +388,7 @@ open class IncrementalJvmCompilerRunner( super.performWorkBeforeCompilation(compilationMode, args) if (compilationMode is CompilationMode.Incremental) { - val destinationDir = args.destinationAsFile - destinationDir.mkdirs() - args.classpathAsList = listOf(destinationDir) + args.classpathAsList + args.classpathAsList = listOf(args.destinationAsFile) + 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 f41bfbb8e21..d7d399cbfa1 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,6 +8,7 @@ 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 @@ -53,27 +54,6 @@ 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) { @@ -87,26 +67,23 @@ object ClasspathEntrySnapshotExternalizer : DataExternalizer( - baseClass = ClassSnapshot::class.java, - inheritorClasses = listOf(AccessibleClassSnapshot::class.java, InaccessibleClassSnapshot::class.java), - inheritorExternalizers = listOf(AccessibleClassSnapshotExternalizer, InaccessibleClassSnapshotExternalizer) +internal object ClassSnapshotExternalizer : DataExternalizer by DelegateDataExternalizer( + types = listOf(AccessibleClassSnapshot::class.java, InaccessibleClassSnapshot::class.java), + typesExternalizers = listOf(AccessibleClassSnapshotExternalizer, InaccessibleClassSnapshotExternalizer) ) -internal object AccessibleClassSnapshotExternalizer : DataExternalizerForSealedClass( - baseClass = AccessibleClassSnapshot::class.java, - inheritorClasses = listOf(KotlinClassSnapshot::class.java, JavaClassSnapshot::class.java), - inheritorExternalizers = listOf(KotlinClassSnapshotExternalizer, JavaClassSnapshotExternalizer) +internal object AccessibleClassSnapshotExternalizer : DataExternalizer by DelegateDataExternalizer( + types = listOf(KotlinClassSnapshot::class.java, JavaClassSnapshot::class.java), + typesExternalizers = listOf(KotlinClassSnapshotExternalizer, JavaClassSnapshotExternalizer) ) -private object KotlinClassSnapshotExternalizer : DataExternalizerForSealedClass( - baseClass = KotlinClassSnapshot::class.java, - inheritorClasses = listOf( +private object KotlinClassSnapshotExternalizer : DataExternalizer by DelegateDataExternalizer( + types = listOf( RegularKotlinClassSnapshot::class.java, PackageFacadeKotlinClassSnapshot::class.java, MultifileClassKotlinClassSnapshot::class.java ), - inheritorExternalizers = listOf( + typesExternalizers = listOf( RegularKotlinClassSnapshotExternalizer, PackageFacadeKotlinClassSnapshotExternalizer, MultifileClassKotlinClassSnapshotExternalizer @@ -185,8 +162,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) - LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).save(output, info.constantsMap) - LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).save(output, info.inlineFunctionsAndAccessorsMap) + MapExternalizer(StringExternalizer, ConstantValueExternalizer).save(output, info.constantsMap) + MapExternalizer(InlineFunctionOrAccessorExternalizer, LongExternalizer).save(output, info.inlineFunctionsAndAccessorsMap) } override fun read(input: DataInput): KotlinClassInfo { @@ -197,8 +174,8 @@ internal object KotlinClassInfoExternalizer : DataExternalizer classHeaderData = ListExternalizer(StringExternalizer).read(input).toTypedArray(), classHeaderStrings = ListExternalizer(StringExternalizer).read(input).toTypedArray(), multifileClassName = NullableValueExternalizer(StringExternalizer).read(input), - constantsMap = LinkedHashMapExternalizer(StringExternalizer, ConstantExternalizer).read(input), - inlineFunctionsAndAccessorsMap = LinkedHashMapExternalizer(StringExternalizer, LongExternalizer).read(input) + constantsMap = MapExternalizer(StringExternalizer, ConstantValueExternalizer).read(input), + inlineFunctionsAndAccessorsMap = MapExternalizer(InlineFunctionOrAccessorExternalizer, 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 90e1a2d5f28..443ece74095 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,9 +9,10 @@ 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.* @@ -96,7 +97,7 @@ object ClassSnapshotter { ) FILE_FACADE, MULTIFILE_CLASS_PART -> PackageFacadeKotlinClassSnapshot( classId, classAbiHash, classMemberLevelSnapshot, - packageMemberNames = kotlinClassInfo.protoData.getNonPrivateMemberNames(includeInlineAccessors = true).toSet() + packageMemberNames = (kotlinClassInfo.protoData as PackagePartProtoData).getNonPrivateMembers().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 61cf45b6358..72d5c064130 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,11 +223,9 @@ 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 = "getInlineProperty_ChangedGetterImpl", scope = "com.example"), - LookupSymbol(name = "setInlineProperty_ChangedSetterImpl", scope = "com.example"), + LookupSymbol(name = "inlineProperty_ChangedGetterImpl", scope = "com.example"), + LookupSymbol(name = "inlineProperty_ChangedSetterImpl", scope = "com.example"), LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SomeClass") ), @@ -238,6 +236,26 @@ 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 new file mode 100644 index 0000000000000000000000000000000000000000..89cf8b70a4e49b86e2c4458ec0938419187fad2b GIT binary patch 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..aa7e950a5f2e4e708f23e0bbd1a10ad63ab0d6c8 GIT binary patch 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..8e631dc4575f4691f59602fd6fd79e411bc7bbc9 GIT binary patch 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) + val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader).map { it.jvmMethodSignature }.toSet() 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) + val inlineFunctionsAndAccessors = inlineFunctionsAndAccessors(binaryClass.classHeader).map { it.jvmMethodSignature }.toSet() //if inline function creates anonymous object then do not try to check that all lambdas are inlined val classVisitor = object : ClassVisitorWithName() { diff --git a/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyInClass/build.log b/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyInClass/build.log index b88aa00d6bf..af5ed8bd041 100644 --- a/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyInClass/build.log +++ b/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyInClass/build.log @@ -8,14 +8,17 @@ Compiling files: src/inline.kt End of files After build round. Marked as dirty by Kotlin: + src/useGetter.kt src/useSetter.kt Exit code: ADDITIONAL_PASS_REQUIRED ------------------------------------------ Cleaning output files: out/production/module/META-INF/module.kotlin_module out/production/module/usage/UseSetterKt.class + out/production/module/usage2/UseGetterKt.class End of files Compiling files: + src/useGetter.kt src/useSetter.kt End of files Exit code: OK @@ -32,14 +35,17 @@ Compiling files: End of files After build round. Marked as dirty by Kotlin: src/useGetter.kt + src/useSetter.kt Exit code: ADDITIONAL_PASS_REQUIRED ------------------------------------------ Cleaning output files: out/production/module/META-INF/module.kotlin_module + out/production/module/usage/UseSetterKt.class out/production/module/usage2/UseGetterKt.class End of files Compiling files: src/useGetter.kt + src/useSetter.kt End of files Exit code: OK ------------------------------------------- \ No newline at end of file +------------------------------------------ diff --git a/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyOnTopLevel/build.log b/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyOnTopLevel/build.log index eac89c80264..a4544891788 100644 --- a/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyOnTopLevel/build.log +++ b/jps/jps-plugin/testData/incremental/pureKotlin/inlinePropertyOnTopLevel/build.log @@ -8,14 +8,17 @@ Compiling files: src/inline.kt End of files After build round. Marked as dirty by Kotlin: + src/useGetter.kt src/useSetter.kt Exit code: ADDITIONAL_PASS_REQUIRED ------------------------------------------ Cleaning output files: out/production/module/META-INF/module.kotlin_module out/production/module/usage/UseSetterKt.class + out/production/module/usage2/UseGetterKt.class End of files Compiling files: + src/useGetter.kt src/useSetter.kt End of files Exit code: OK @@ -32,14 +35,17 @@ Compiling files: End of files After build round. Marked as dirty by Kotlin: src/useGetter.kt + src/useSetter.kt Exit code: ADDITIONAL_PASS_REQUIRED ------------------------------------------ Cleaning output files: out/production/module/META-INF/module.kotlin_module + out/production/module/usage/UseSetterKt.class out/production/module/usage2/UseGetterKt.class End of files Compiling files: src/useGetter.kt + src/useSetter.kt End of files Exit code: OK ------------------------------------------- \ No newline at end of file +------------------------------------------ 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 ff445459288..81bdc23d193 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/benchmarks/GenerateIrRuntime.kt @@ -326,7 +326,7 @@ class GenerateIrRuntime { buildHistoryFile = buildHistoryFile, modulesApiHistory = EmptyModulesApiHistory ) - compiler.compile(allFiles, args, MessageCollector.NONE, providedChangedFiles = null) + compiler.compile(allFiles, args, MessageCollector.NONE, changedFiles = null) } val cleanBuildTime = System.nanoTime() - cleanBuildStart