diff --git a/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt b/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt index 025c7fa8cca..746300b280a 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/KotlinClassInfo.kt @@ -5,12 +5,12 @@ package org.jetbrains.kotlin.incremental +import com.intellij.util.io.DataExternalizer import org.jetbrains.kotlin.incremental.ClassNodeSnapshotter.snapshotClassExcludingMembers -import org.jetbrains.kotlin.incremental.ClassNodeSnapshotter.snapshotField import org.jetbrains.kotlin.incremental.ClassNodeSnapshotter.snapshotMethod import org.jetbrains.kotlin.incremental.ClassNodeSnapshotter.sortClassMembers import org.jetbrains.kotlin.incremental.KotlinClassInfo.ExtraInfo -import org.jetbrains.kotlin.incremental.storage.ProtoMapValue +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 @@ -45,19 +45,32 @@ class KotlinClassInfo( class ExtraInfo( /** - * Snapshot of the class excluding its fields and methods and Kotlin metadata (iff classKind == [KotlinClassHeader.Kind.CLASS]). + * Snapshot of the class excluding its fields and methods and Kotlin metadata. It is not null iff + * [classKind] == [KotlinClassHeader.Kind.CLASS]. * - * For example, the class's annotations which are currently not captured by Kotlin metadata (KT-57919) will be captured here. + * Note: Kotlin metadata is excluded because [ExtraInfo] is meant to contain information that supplements Kotlin metadata. (We have + * a separate logic for comparing protos constructed from Kotlin metadata. That logic considers only changes in protos/Kotlin + * metadata that are important for incremental compilation. If we don't exclude Kotlin metadata here, we might report a change in + * Kotlin metadata even when the change is not important for incremental compilation.) * - * Note: It also excludes Kotlin metadata as [ExtraInfo] should only contain info not yet captured in Kotlin metadata. + * TODO(KT-59292): Consider removing this info once class annotations are included in Kotlin metadata. */ val classSnapshotExcludingMembers: Long?, - /** Snapshots of the class's constants (including their values). The map's keys are the constants' names. */ + /** + * Snapshots of the class's non-private constants. + * + * Each entry maps a constant's name to the hash of its value. + */ val constantSnapshots: Map, - /** Snapshots of the class's inline functions and property accessors (including their implementation). */ - val inlineFunctionOrAccessorSnapshots: Map + /** + * Snapshots of the class's non-private inline functions and property accessors. + * + * Each entry maps an inline function or property accessor to the hash of its corresponding method in the bytecode (including the + * method's body). + */ + val inlineFunctionOrAccessorSnapshots: Map, ) val className: JvmClassName by lazy { JvmClassName.byClassId(classId) } @@ -122,19 +135,29 @@ class KotlinClassInfo( } private fun getExtraInfo(classHeader: KotlinClassHeader, classContents: ByteArray): ExtraInfo { - // Get the list of (non-private) inline functions and accessors from Kotlin class metadata, then find and snapshot them in the bytecode. - // Note: - // - Some of them may not be found in the bytecode. Specifically, internal/private inline functions/accessors 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 class metadata (also in the source file), but not in the - // bytecode. When that happens, we will ignore those methods. It is safe to ignore because the methods are not declared in the bytecode - // and therefore can't be referenced. - // - Look for private methods as well because a *public* inline function/accessor may have a *private* corresponding method in the - // bytecode (see `InlineOnlyKt.isInlineOnlyPrivateInBytecode`). - val inlineFunctionsAndAccessors: List = inlineFunctionsAndAccessors(classHeader, excludePrivateMembers = true) - val inlineFunctionOrAccessorSignatures: Map = - inlineFunctionsAndAccessors.associateBy { it.jvmMethodSignature } + val inlineFunctionsAndAccessors: Map = + inlineFunctionsAndAccessors(classHeader, excludePrivateMembers = true).associateBy { it.jvmMethodSignature } + // 1. Create a ClassNode that will contain only required info + val classNode = ClassNode() + + // 2. Load the class's contents into the ClassNode, keeping only info that is required to compute `ExtraInfo`: + // - Keep only fields that are non-private constants + // - Keep only methods that are non-private inline functions/accessors + // + Do not filter out private methods because a *non-private* inline function/accessor may have a *private* corresponding method + // in the bytecode (see `InlineOnlyKt.isInlineOnlyPrivateInBytecode`) + // + Do not filter out method bodies + val classReader = ClassReader(classContents) + val selectiveClassVisitor = SelectiveClassVisitor( + classNode, + shouldVisitField = { _: JvmMemberSignature.Field, isPrivate: Boolean, isConstant: Boolean -> + !isPrivate && isConstant + }, + shouldVisitMethod = { method: JvmMemberSignature.Method, _: Boolean -> + // Do not filter out private methods (see above comment) + method in inlineFunctionsAndAccessors.keys + } + ) val parsingOptions = if (inlineFunctionsAndAccessors.isNotEmpty()) { // Do not pass (SKIP_CODE, SKIP_DEBUG) as method bodies and debug info (e.g., line numbers) are important for inline // functions/accessors @@ -144,54 +167,64 @@ private fun getExtraInfo(classHeader: KotlinClassHeader, classContents: ByteArra // inline functions/accessors ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG } + classReader.accept(selectiveClassVisitor, parsingOptions) - // Load class contents into a `ClassNode`. - // Note that we'll only load methods that are inline functions/accessors (including private methods -- see comment at the top of - // `getExtraInfo`) as we don't need to snapshot the other methods when computing `ExtraInfo`. - val classNode = ClassNode() - val classReader = ClassReader(classContents) - classReader.accept(SkipMethodClassVisitor(classNode) { it !in inlineFunctionOrAccessorSignatures.keys }, parsingOptions) + // 3. Sort fields and methods as their order is not important sortClassMembers(classNode) - // Snapshot the class excluding its fields and methods and metadata + // 4. Snapshot the class val classSnapshotExcludingMembers = if (classHeader.kind == KotlinClassHeader.Kind.CLASS) { // Also exclude Kotlin metadata (see `ExtraInfo.classSnapshotExcludingMembers`'s kdoc) snapshotClassExcludingMembers(classNode, alsoExcludeKotlinMetaData = true) } else null - // Snapshot constants - fun FieldNode.isPrivate() = (access and Opcodes.ACC_PRIVATE) != 0 - fun FieldNode.isStaticFinal() = (access and (Opcodes.ACC_STATIC or Opcodes.ACC_FINAL)) == (Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) + val constantSnapshots: Map = classNode.fields.associate { fieldNode -> + // Note: `fieldNode` is a constant because we kept only fields that are (non-private) constants in `classNode` + fieldNode.name to (fieldNode.value?.let { ConstantValueExternalizer.toByteArray(it).hashToLong() } ?: 0L) + } - val constantSnapshots: Map = classNode.fields - .filter { !it.isPrivate() && it.isStaticFinal() } - .associate { it.name to snapshotField(it) } - - // Snapshot inline functions and accessors - fun MethodNode.signature() = JvmMemberSignature.Method(name = name, desc = desc) - - val inlineFunctionOrAccessorSnapshots: Map = classNode.methods - .associate { methodNode -> - // `methodNode` must be an inline function/accessor because we loaded only inline functions/accessors into `classNode` - inlineFunctionOrAccessorSignatures[methodNode.signature()]!! to snapshotMethod(methodNode, classNode.version) - } + val inlineFunctionOrAccessorSnapshots: Map = classNode.methods.associate { methodNode -> + // Note: + // - Each of `classNode.methods` (`methodNode`) is an inline function/accessor because we kept only methods that are (non-private) + // inline functions/accessors in `classNode`. + // - Not all inline functions/accessors have a corresponding method in the bytecode (i.e., it's possible that + // `classNode.methods.size < inlineFunctionsAndAccessors.size`). Specifically, internal/private inline functions/accessors 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 + // class metadata (also in the source file), but not in the bytecode. However, we can safely ignore those + // inline functions/accessors because they are not declared in the bytecode and therefore can't be referenced. + val methodSignature = JvmMemberSignature.Method(name = methodNode.name, desc = methodNode.desc) + inlineFunctionsAndAccessors[methodSignature]!! to snapshotMethod(methodNode, classNode.version) + } return ExtraInfo(classSnapshotExcludingMembers, constantSnapshots, inlineFunctionOrAccessorSnapshots) } -/** [ClassVisitor] which skips visiting methods where `[shouldSkipMethod] == true`. */ -private class SkipMethodClassVisitor( +/** + * [ClassVisitor] which visits only members satisfying the given criteria (`[shouldVisitField] == true` or `[shouldVisitMethod] == true`). + */ +class SelectiveClassVisitor( cv: ClassVisitor, - private val shouldSkipMethod: (JvmMemberSignature.Method) -> Boolean, + private val shouldVisitField: (JvmMemberSignature.Field, isPrivate: Boolean, isConstant: Boolean) -> Boolean, + private val shouldVisitMethod: (JvmMemberSignature.Method, isPrivate: Boolean) -> Boolean, ) : ClassVisitor(Opcodes.API_VERSION, cv) { - override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { - return if (shouldSkipMethod(JvmMemberSignature.Method(name, desc))) { - null - } else { - cv.visitMethod(access, name, desc, signature, exceptions) - } + override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? { + return if (shouldVisitField(JvmMemberSignature.Field(name, desc), access.isPrivate(), access.isStaticFinal())) { + cv.visitField(access, name, desc, signature, value) + } else null } + + override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { + return if (shouldVisitMethod(JvmMemberSignature.Method(name, desc), access.isPrivate())) { + cv.visitMethod(access, name, desc, signature, exceptions) + } else null + } + + private fun Int.isPrivate() = (this and Opcodes.ACC_PRIVATE) != 0 + + private fun Int.isStaticFinal() = (this and (Opcodes.ACC_STATIC or Opcodes.ACC_FINAL)) == (Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) + } /** Computes the snapshot of a Java class represented by a [ClassNode]. */ @@ -227,7 +260,7 @@ object ClassNodeSnapshotter { fun snapshotMethod(methodNode: MethodNode, classVersion: Int): Long { val classNode = emptyClass() - classNode.version = classVersion // Class version is required for method bodies (see KT-38857) + classNode.version = classVersion // Class version is required when working with methods (without it, ASM may fail -- see KT-38857) classNode.methods.add(methodNode) return snapshotClass(classNode) } @@ -245,10 +278,29 @@ object ClassNodeSnapshotter { private fun emptyClass() = ClassNode().also { // A name is required - it.name = "EmptyClass" + it.name = "SomeClass" } } +/** + * [DataExternalizer] for the value of a constant. + * + * A constant's value must be not-null and must be one of the following types: Integer, Long, Float, Double, String (see the javadoc of + * [ClassVisitor.visitField]). + * + * Side note: The value of a Boolean constant is represented as an Integer (0, 1) value. + */ +private 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) +) + fun ByteArray.hashToLong(): Long { // Note: The returned type `Long` is 64-bit, but we currently don't have a good 64-bit hash function. // The method below uses `md5` which is 128-bit and converts it to `Long`. diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClassFile.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClassFile.kt index e1cf5b88d4d..8768d3ee6b0 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClassFile.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClassFile.kt @@ -42,5 +42,7 @@ class ClassFileWithContents( @Suppress("unused") val classFile: ClassFile, val contents: ByteArray ) { - val classInfo: BasicClassInfo = BasicClassInfo.compute(contents) + val classInfo: BasicClassInfo by lazy { + BasicClassInfo.compute(contents) + } } diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt index 0839ea144ff..aad24c862f3 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathChangesComputer.kt @@ -248,17 +248,20 @@ object ClasspathChangesComputer { // IncrementalJvmCache currently doesn't use the `KotlinClassInfo.extraInfo.classSnapshotExcludingMembers` info when comparing // classes, so we need to do it here. - // TODO(KT-58289): Ensure IncrementalJvmCache uses that info when comparing classes. + // TODO(KT-59292): Ensure IncrementalJvmCache uses that info when comparing classes, so we can remove this code. val currentClassSnapshotsExcludingMembers = currentClassSnapshots .associate { it.classId to it.classMemberLevelSnapshot!!.extraInfo.classSnapshotExcludingMembers } .filter { it.value != null } - val previousClassSnapshotsExcludingMembers = previousClassSnapshots - .associate { it.classId to it.classMemberLevelSnapshot!!.extraInfo.classSnapshotExcludingMembers } - .filter { it.value != null } - previousClassSnapshotsExcludingMembers.keys.intersect(currentClassSnapshotsExcludingMembers.keys).forEach { - if (previousClassSnapshotsExcludingMembers[it]!! != currentClassSnapshotsExcludingMembers[it]!!) { + previousClassSnapshots.forEach { previousClassSnapshot -> + val classId = previousClassSnapshot.classId + val currentClassSnapshotExcludingMember = currentClassSnapshotsExcludingMembers[classId] + val previousClassSnapshotExcludingMembers = + previousClassSnapshot.classMemberLevelSnapshot!!.extraInfo.classSnapshotExcludingMembers + if (currentClassSnapshotExcludingMember != null && previousClassSnapshotExcludingMembers != null + && currentClassSnapshotExcludingMember != previousClassSnapshotExcludingMembers + ) { // `areSubclassesAffected = false` as we don't need to compute impacted symbols at this step - changesCollector.collectSignature(fqName = it.asSingleFqName(), areSubclassesAffected = false) + changesCollector.collectSignature(fqName = classId.asSingleFqName(), areSubclassesAffected = false) } } 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 51acefd0659..96e1cc713bc 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 @@ -177,7 +177,7 @@ internal object KotlinClassInfoExternalizer : DataExternalizer } } -internal object ExtraInfoExternalizer : DataExternalizer { +private object ExtraInfoExternalizer : DataExternalizer { override fun save(output: DataOutput, info: KotlinClassInfo.ExtraInfo) { NullableValueExternalizer(LongExternalizer).save(output, info.classSnapshotExcludingMembers) 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 0f30fc5928a..f903b4aa3cf 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 @@ -17,18 +17,18 @@ import org.jetbrains.kotlin.incremental.ClassNodeSnapshotter.sortClassMembers 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.SelectiveClassVisitor import org.jetbrains.kotlin.incremental.classpathDiff.ClassSnapshotGranularity.CLASS_MEMBER_LEVEL import org.jetbrains.kotlin.incremental.hashToLong import org.jetbrains.kotlin.incremental.storage.toByteArray import org.jetbrains.kotlin.konan.file.use import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader.Kind.* +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMemberSignature import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.org.objectweb.asm.ClassReader -import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.tree.ClassNode import java.io.Closeable import java.io.File -import java.util.zip.ZipEntry import java.util.zip.ZipFile /** Computes a [ClasspathEntrySnapshot] of a classpath entry (directory or jar). */ @@ -163,23 +163,30 @@ object ClassSnapshotter { // 2. Remove non-ABI info from the full class // Note that for incremental compilation to be correct, all ABI info must be collected exhaustively (now and in the future when // there are updates to Java/ASM), whereas it is acceptable if non-ABI info is not removed completely. - // In the following, we will use the second approach as it is safer and easier. + // Therefore, we will use the second approach as it is safer and easier. + // 1. Create a ClassNode that will contain ABI info of the class val classNode = ClassNode() - val classReader = ClassReader(classFile.contents) + // 2. Load the class's contents into the ClassNode, removing non-ABI info: + // - Remove private fields and methods + // - Remove method bodies + // - [Not yet implemented] Ignore fields' values except for constants // Note the `parsingOptions` passed to `classReader`: - // - Pass SKIP_CODE as method bodies are not important - // - Do not pass SKIP_DEBUG as debug info (e.g., method parameter names) may be important - classReader.accept(classNode, ClassReader.SKIP_CODE) + // - Pass SKIP_CODE as we want to remove method bodies + // - Do not pass SKIP_DEBUG as debug info (e.g., method parameter names) may be important + val classReader = ClassReader(classFile.contents) + val selectiveClassVisitor = SelectiveClassVisitor( + classNode, + shouldVisitField = { _: JvmMemberSignature.Field, isPrivate: Boolean, _: Boolean -> !isPrivate }, + shouldVisitMethod = { _: JvmMemberSignature.Method, isPrivate: Boolean -> !isPrivate } + ) + classReader.accept(selectiveClassVisitor, ClassReader.SKIP_CODE) + + // 3. Sort fields and methods as their order is not important sortClassMembers(classNode) - // Remove private fields and methods - fun Int.isPrivate() = (this and Opcodes.ACC_PRIVATE) != 0 - classNode.fields.removeIf { it.access.isPrivate() } - classNode.methods.removeIf { it.access.isPrivate() } - - // Snapshot the class + // 4. Snapshot the class val classMemberLevelSnapshot = if (granularity == CLASS_MEMBER_LEVEL) { JavaClassMemberLevelSnapshot( classAbiExcludingMembers = JavaElementSnapshot(classNode.name, snapshotClassExcludingMembers(classNode)), @@ -190,6 +197,7 @@ object ClassSnapshotter { null } val classAbiHash = if (granularity == CLASS_MEMBER_LEVEL) { + // We already have the class-member-level snapshot, so we can just hash it instead of snapshotting the class again JavaClassMemberLevelSnapshotExternalizer.toByteArray(classMemberLevelSnapshot!!).hashToLong() } else { snapshotClass(classNode) 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 4fad567f052..7918ee5baf4 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 @@ -261,7 +261,7 @@ class KotlinOnlyClasspathChangesComputerTest : ClasspathChangesComputerTest() { fun testRenameFileFacade() { // Check that classpath changes computation doesn't fail. // Ideally, the returned changes should be empty (renaming a file facade alone shouldn't cause any `LookupSymbol`s to change), but - // it is currently not the case. However, this is just a small efficiency, not a serious bug. + // it is currently not the case. However, this is just a small inefficiency, not an IC-correctness bug. val changes = computeClasspathChanges(File(testDataDir, "KotlinOnly/testRenameFileFacade/src"), tmpDir) Changes( lookupSymbols = setOf( @@ -275,6 +275,10 @@ class KotlinOnlyClasspathChangesComputerTest : ClasspathChangesComputerTest() { /** Regression test for KT-58289.*/ @Test fun testChangedAnnotations() { + // Note: Currently we detect changes to annotations on Kotlin classes, but not annotations on properties and functions, so the + // following changes are missing: `SomeClass.propertyWithChangedAnnotation` and `SomeClass.functionWithChangedAnnotation`. + // Once all annotations are added to Kotlin metadata (KT-57919), we should be able to detect changes to them (i.e., we'll need to + // update this test assertion then). val changes = computeClasspathChanges(File(testDataDir, "KotlinOnly/testChangedAnnotations/src"), tmpDir) Changes( lookupSymbols = setOf( diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotTestCommon.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotTestCommon.kt index b7812f004d9..4f65460c2f5 100644 --- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotTestCommon.kt +++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotTestCommon.kt @@ -138,6 +138,8 @@ abstract class ClasspathSnapshotTestCommon { "-classpath", (listOf(srcDir) + classpath).joinToString(File.pathSeparator) { it.path } ) runCommandInNewProcess(commandAndArgs) + + classesDir.resolve("META-INF").deleteRecursively() } private fun compileJava(srcDir: File, classesDir: File, classpath: List): List { diff --git a/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotterTest/java/testSimpleClass/expected-snapshot/com/example/SimpleClass.json b/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotterTest/java/testSimpleClass/expected-snapshot/com/example/SimpleClass.json index 80be85ca59f..59d878d78f3 100644 --- a/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotterTest/java/testSimpleClass/expected-snapshot/com/example/SimpleClass.json +++ b/compiler/incremental-compilation-impl/testData/org/jetbrains/kotlin/incremental/classpathDiff/ClasspathSnapshotterTest/java/testSimpleClass/expected-snapshot/com/example/SimpleClass.json @@ -12,7 +12,7 @@ }, "local": false }, - "classAbiHash": -6381547343043280587, + "classAbiHash": 2881075740228300324, "classMemberLevelSnapshot": { "classAbiExcludingMembers": { "name": "com/example/SimpleClass", @@ -21,17 +21,17 @@ "fieldsAbi": [ { "name": "publicField", - "abiHash": 8370167348140025367 + "abiHash": -2698261112133196916 } ], "methodsAbi": [ { "name": "\u003cinit\u003e", - "abiHash": 9200648777950343158 + "abiHash": 6894913661751208844 }, { "name": "publicMethod", - "abiHash": -2847179652577921235 + "abiHash": -3018578973377534537 } ] },