diff --git a/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt b/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt index c768fa3a8cb..afdac00ddf3 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/AbstractIncrementalCache.kt @@ -118,7 +118,12 @@ abstract class AbstractIncrementalCache( } } - protected fun addToClassStorage(proto: ProtoBuf.Class, nameResolver: NameResolver, srcFile: File) { + /** + * Updates class storage based on the given class proto. + * + * The `srcFile` argument may be `null` (e.g., if we are processing .class files in jars where source files are not available). + */ + protected fun addToClassStorage(proto: ProtoBuf.Class, nameResolver: NameResolver, srcFile: File?) { val supertypes = proto.supertypes(TypeTable(proto.typeTable)) val parents = supertypes.map { nameResolver.getClassId(it.className).asSingleFqName() } .filter { it.asString() != "kotlin.Any" } @@ -131,7 +136,7 @@ abstract class AbstractIncrementalCache( removedSupertypes.forEach { subtypesMap.removeValues(it, setOf(child)) } supertypesMap[child] = parents - classFqNameToSourceMap[child] = srcFile + srcFile?.let { classFqNameToSourceMap[child] = it } classAttributesMap[child] = ICClassesAttributes(ProtoBuf.Modality.SEALED == Flags.MODALITY.get(proto.flags)) } diff --git a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt index c3ffb1e17c1..f423de274c9 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/IncrementalJvmCache.kt @@ -184,7 +184,9 @@ open class IncrementalJvmCache( KotlinClassHeader.Kind.CLASS -> { if (sourceFiles != null) { assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" } - addToClassStorage(kotlinClassInfo, sourceFiles.first()) + addToClassStorage(kotlinClassInfo, sourceFiles.single()) + } else { + addToClassStorage(kotlinClassInfo, null) } protoMap.process(kotlinClassInfo, changesCollector) @@ -196,10 +198,10 @@ open class IncrementalJvmCache( } } - fun saveJavaClassProto(source: File, serializedJavaClass: SerializedJavaClass, collector: ChangesCollector) { + fun saveJavaClassProto(source: File?, serializedJavaClass: SerializedJavaClass, collector: ChangesCollector) { val jvmClassName = JvmClassName.byClassId(serializedJavaClass.classId) javaSourcesProtoMap.process(jvmClassName, serializedJavaClass, collector) - sourceToClassesMap.add(source, jvmClassName) + source?.let { sourceToClassesMap.add(source, jvmClassName) } val (proto, nameResolver) = serializedJavaClass.toProtoData() addToClassStorage(proto, nameResolver, source) // collector.addJavaProto(ClassProtoData(proto, nameResolver)) @@ -501,7 +503,7 @@ open class IncrementalJvmCache( value.dumpCollection() } - private fun addToClassStorage(classInfo: KotlinClassInfo, srcFile: File) { + private fun addToClassStorage(classInfo: KotlinClassInfo, srcFile: File?) { val (nameResolver, proto) = JvmProtoBufUtil.readClassDataFrom(classInfo.classHeaderData, classInfo.classHeaderStrings) addToClassStorage(proto, nameResolver, srcFile) } diff --git a/build-common/src/org/jetbrains/kotlin/incremental/JavaClassDescriptorCreator.kt b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassDescriptorCreator.kt new file mode 100644 index 00000000000..09c1d88a26f --- /dev/null +++ b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassDescriptorCreator.kt @@ -0,0 +1,225 @@ +/* + * Copyright 2010-2021 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 org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.SourceFile +import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies +import org.jetbrains.kotlin.load.java.JavaClassFinder +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor +import org.jetbrains.kotlin.load.java.sources.JavaSourceElement +import org.jetbrains.kotlin.load.java.sources.JavaSourceElementFactory +import org.jetbrains.kotlin.load.java.structure.JavaAnnotation +import org.jetbrains.kotlin.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.JavaElement +import org.jetbrains.kotlin.load.java.structure.JavaPackage +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext +import org.jetbrains.kotlin.load.kotlin.DeserializationComponentsForJava.Companion.createModuleData +import org.jetbrains.kotlin.load.kotlin.KotlinClassFinder +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.DescriptorSerializer +import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol +import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInsResourceLoader +import java.io.InputStream + +/** Creates [JavaClassDescriptor]s of Java classes. */ +object JavaClassDescriptorCreator { + + /** + * Creates [JavaClassDescriptor]s of the given Java classes. + * + * Note that creating a [JavaClassDescriptor] for a nested class will require accessing the outer class (and possibly vice versa). + * Therefore, outer classes and nested classes must be passed together in one invocation of this method. + */ + fun create(classIds: List, classesContents: List): List { + val binaryJavaClasses = classIds.mapIndexed { index, classId -> + createBinaryJavaClass(classId, classesContents[index]) + } + val moduleDescriptor = createModuleData( + kotlinClassFinder = NoOpKotlinClassFinder, + jvmBuiltInsKotlinClassFinder = JvmBuiltInsKotlinClassFinder(), + javaClassFinder = BinaryJavaClassFinder(binaryJavaClasses), + moduleName = JavaClassDescriptorCreator::class.java.simpleName, + errorReporter = ThrowImmediatelyErrorReporter, + javaSourceElementFactory = NoSourceJavaSourceElementFactory + ).deserializationComponentsForJava.components.moduleDescriptor + + return classIds.map { classId -> + moduleDescriptor.findClassAcrossModuleDependencies(classId) as? JavaClassDescriptor + ?: error("Failed to create JavaClassDescriptor for class '$classId'") + } + } +} + +private fun createBinaryJavaClass(classId: ClassId, classContents: ByteArray): BinaryJavaClass { + val context = ClassifierResolutionContext { + null // TODO: Implement this? + } + val outerClass: JavaClass? = null // TODO: Compute outer class? + val innerClassFinder: ((Name) -> JavaClass?) = { _ -> + null // TODO: Implement this? + } + + return try { + BinaryJavaClass( + virtualFile = null, + fqName = classId.asSingleFqName(), + context = context, + signatureParser = BinaryClassSignatureParser(), + access = 0, + outerClass = outerClass, + classContent = classContents, + innerClassFinder = innerClassFinder + ) + } catch (e: NoSuchMethodError) { + // When running unit tests, the above call currently fails as JavaClassDescriptorCreator and BinaryJavaClass are located in + // different jars (kotlin-build-common.jar and kotlin-compiler-embeddable.jar), and in the second jar, `com.intellij.*` classes are + // repackaged as `org.jetbrains.kotlin.com.intellij.*`. The `virtualFile` argument above (even though it is null) has type + // `com.intellij.openapi.vfs.VirtualFile`, which has been repackaged, so the method signatures won't match at run time. + // In integrations tests or regular builds, JavaClassDescriptorCreator and BinaryJavaClass are both located in + // kotlin-compiler-embeddable.jar with all references renamed together, so there are no issues. + // We use reflection here as a workaround for unit tests. + val binaryJavaClass = BinaryJavaClass::class.java + val constructor = binaryJavaClass.constructors.sortedBy { it.parameters.size }[0] + return constructor.newInstance( + /* virtualFile */ null, + /* fqName */ classId.asSingleFqName(), + /* context */ context, + /* signatureParser */ BinaryClassSignatureParser(), + /* access */ 0, + /* outerClass */ outerClass, + /* classContent */ classContents, + /* innerClassFinder */ innerClassFinder + ) as BinaryJavaClass + } +} + +/** + * [JavaClassFinder] that returns results based on the given [BinaryJavaClass] list. + * + * Note that some returned results are fake/empty (similar to ReflectJavaClassFinder). + * TODO: Revise and see if there are any correctness issues. + */ +private class BinaryJavaClassFinder(binaryJavaClasses: List) : JavaClassFinder { + + private val nameToJavaClass: Map = binaryJavaClasses.associateBy { it.fqName } + + override fun findClass(request: JavaClassFinder.Request): JavaClass? { + return nameToJavaClass[request.classId.asSingleFqName()] + } + + override fun findPackage(fqName: FqName): JavaPackage { + return object : JavaPackage { + + override val fqName: FqName + get() = fqName + + override val subPackages: Collection + get() = emptyList() + + override val annotations: Collection + get() = emptyList() + + override val isDeprecatedInJavaDoc: Boolean + get() = false + + override fun getClasses(nameFilter: (Name) -> Boolean): Collection = emptyList() + + override fun findAnnotation(fqName: FqName): JavaAnnotation? = null + } + } + + override fun knownClassNamesInPackage(packageFqName: FqName): Set? = null +} + +/** [KotlinClassFinder] that returns no results (e.g., when we know that the classes are not Kotlin classes). */ +private object NoOpKotlinClassFinder : KotlinClassFinder { + override fun findKotlinClassOrContent(classId: ClassId): KotlinClassFinder.Result? = null + override fun findKotlinClassOrContent(javaClass: JavaClass): KotlinClassFinder.Result? = null + override fun findMetadata(classId: ClassId): InputStream? = null + override fun hasMetadataPackage(fqName: FqName): Boolean = false + override fun findBuiltInsData(packageFqName: FqName): InputStream? = null +} + +/** [KotlinClassFinder] for Kotlin JVM built-in classes. */ +private class JvmBuiltInsKotlinClassFinder : KotlinClassFinder { + override fun findKotlinClassOrContent(classId: ClassId): KotlinClassFinder.Result? = null + override fun findKotlinClassOrContent(javaClass: JavaClass): KotlinClassFinder.Result? = null + override fun findMetadata(classId: ClassId): InputStream? = null + override fun hasMetadataPackage(fqName: FqName): Boolean = false + + private val builtInsResourceLoader by lazy { BuiltInsResourceLoader() } + + override fun findBuiltInsData(packageFqName: FqName): InputStream? { + // Same as ReflectKotlinClassFinder.findBuiltInsData + return if (packageFqName.startsWith(StandardNames.BUILT_INS_PACKAGE_NAME)) { + builtInsResourceLoader.loadResource(BuiltInSerializerProtocol.getBuiltInsFilePath(packageFqName)) + } else null + } +} + +/** [ErrorReporter] that throws errors as soon as they are reported. */ +private object ThrowImmediatelyErrorReporter : ErrorReporter { + + override fun reportIncompleteHierarchy(descriptor: ClassDescriptor, unresolvedSuperClasses: MutableList) { + // BinaryJavaClassFinder doesn't have the whole classpath so it is unable to resolve classes such as java.lang.Object. + // Ignore this error for now. + // TODO: Revisit later to see how we can address this or if we can safely ignore this error. + } + + override fun reportCannotInferVisibility(descriptor: CallableMemberDescriptor) { + error("Cannot infer visibility for $descriptor") + } +} + +/** [JavaSourceElementFactory] that doesn't provide a source file for a [JavaElement] (e.g., because the source file is not available). */ +private object NoSourceJavaSourceElementFactory : JavaSourceElementFactory { + + private class NoSourceJavaElement(override val javaElement: JavaElement) : JavaSourceElement { + override fun getContainingFile(): SourceFile = SourceFile.NO_SOURCE_FILE + } + + override fun source(javaElement: JavaElement): JavaSourceElement = NoSourceJavaElement(javaElement) +} + +/** Similar to [JavaClassDescriptor.convertToProto] but without an associated source file. */ +fun JavaClassDescriptor.toSerializedJavaClass(): SerializedJavaClass { + val extension = JavaClassesSerializerExtension() + + val descriptorSerializer = try { + DescriptorSerializer.create( + descriptor = this, + extension = extension, + parentSerializer = null, + project = null + ) + } catch (e: NoSuchMethodError) { + // When running unit tests, the above call currently fails as the `project` argument above has type + // `com.intellij.openapi.project.Project`, which is repackaged as `org.jetbrains.kotlin.com.intellij.openapi.project.Project` in + // kotlin-compiler-embeddable.jar (see the comment at the createBinaryJavaClass method for more context). + // We use reflection here as a workaround for unit tests. + val createMethod = DescriptorSerializer::class.java.methods.single { it.name == "create" } + createMethod.invoke( + /* static method */ null, + /* descriptor */ this, + /* extension */ extension, + /* parentSerializer */ null, + /* project */ null + ) as DescriptorSerializer + } + + val classProto = descriptorSerializer.classProto(this).build() + val (stringTable, qualifiedNameTable) = extension.stringTable.buildProto() + + return SerializedJavaClass(classProto, stringTable, qualifiedNameTable) +} diff --git a/build-common/src/org/jetbrains/kotlin/incremental/JavaClassName.kt b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassName.kt new file mode 100644 index 00000000000..752b6e69aac --- /dev/null +++ b/build-common/src/org/jetbrains/kotlin/incremental/JavaClassName.kt @@ -0,0 +1,210 @@ +/* + * Copyright 2010-2021 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.openapi.util.Ref +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.ClassVisitor +import org.jetbrains.org.objectweb.asm.Opcodes + +/** + * Information about the name of a compiled Java class regarding its nesting level. + * + * A [JavaClassName] can be: + * - [TopLevelClass] + * - [NestedClass] (https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html) + * + * A [NestedClass] can be: + * - [NestedNonLocalClass] + * - [LocalClass] (https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html) + */ +sealed class JavaClassName( + + /** The full name of this class (e.g., "com/example/Foo$Bar"). */ + val name: String +) { + + /** The package name of this class (e.g., "com/example"). */ + val packageName: String + get() = name.substringBeforeLast('/', "") + + companion object { + + fun compute(classContents: ByteArray): JavaClassName { + val nameRef = Ref.create() + val isTopLevelRef = Ref.create() + val outerNameRef = Ref.create() + val isAnonymousInnerClassRef = Ref.create() + val isSyntheticInnerClassRef = Ref.create() + + ClassReader(classContents).accept(object : ClassVisitor(Opcodes.API_VERSION) { + override fun visit( + version: Int, access: Int, name: String, + signature: String?, superName: String?, interfaces: Array? + ) { + nameRef.set(name) + } + + override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) { + if (name == nameRef.get()!!) { + isTopLevelRef.set(false) + outerNameRef.set(outerName) + isAnonymousInnerClassRef.set(innerName == null) + isSyntheticInnerClassRef.set((access and Opcodes.ACC_SYNTHETIC) != 0) + } + } + }, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES) + + val name = nameRef.get()!! + val isTopLevel = isTopLevelRef.get() ?: true + val outerName = outerNameRef.get() + + return when { + isTopLevel -> TopLevelClass(name) + outerName != null -> NestedNonLocalClass( + name, + outerName, + isAnonymousInnerClassRef.get()!!, + isSyntheticInnerClassRef.get()!! + ) + else -> LocalClass(name) + } + } + } +} + +/** See [JavaClassName]. */ +class TopLevelClass(name: String) : JavaClassName(name) { + + /** + * The simple name of this class (e.g., the simple name of class "com/example/Foo" is "Foo", the simple name of class + * "com/example/ClassWith$Sign" is "ClassWith$Sign"). + */ + val simpleName: String + get() = name.substringAfterLast('/') +} + +/** See [JavaClassName]. */ +sealed class NestedClass(name: String) : JavaClassName(name) + +/** See [JavaClassName]. */ +class NestedNonLocalClass( + name: String, + + /** + * The full name of the outer class of this class (e.g., the outer name of "com/example/OuterClass$NestedClass" is + * "com/example/OuterClass", the outer name of class "com/example/OuterClassWith$Sign$NestedClassWith$Sign" is + * "com/example/OuterClassWith$Sign"). + * + * The outer class can be of any type ([TopLevelClass], [NestedNonLocalClass], or [LocalClass]). + */ + val outerName: String, + + /** + * Whether this class is an anonymous class. + * + * Note: Even though an anonymous class has no name in the source code, it always has a (not-null, not-empty) name in the compiled + * class. Therefore, [simpleName] is not `null` and not empty even for anonymous classes. + * */ + val isAnonymous: Boolean, + + /** Whether this class is a synthetic class. */ + val isSynthetic: Boolean +) : NestedClass(name) { + + init { + check(name.startsWith("$outerName\$")) + } + + /** + * The simple name of this class (e.g., the simple name of "com/example/OuterClass$NestedClass" is "NestedClass", the simple name of + * class "com/example/OuterClassWith$Sign$NestedClassWith$Sign" is "NestedClassWith$Sign"). + * + * Note: [simpleName] is not `null` and not empty even for anonymous classes (see [isAnonymous]). + */ + val simpleName: String + get() = name.substring("$outerName\$".length) +} + +/** See [JavaClassName]. */ +class LocalClass(name: String) : NestedClass(name) + +/** + * Computes [ClassId]s of the given Java classes. + * + * Note that creating a [ClassId] for a nested class will require accessing the outer class for 2 reasons: + * - To disambiguate any '$' characters in the class name (e.g., "com/example/OuterClassWith$Sign$NestedClassWith$Sign"). + * - To determine whether a class is a local class (a nested class of a local class is also considered local, see [ClassId]'s kdoc). + * + * Therefore, outer classes and nested classes must be passed together in one invocation of this method. + */ +fun computeJavaClassIds(javaClassNames: List): List { + val nameToJavaClassName: Map = javaClassNames.associateBy { it.name } + val nameToClassId: MutableMap = nameToJavaClassName.mapValues { null }.toMutableMap() + + fun getOrCreateClassId(className: String): ClassId { + val classInfo = nameToJavaClassName[className] ?: error("Class name not found: $className") + val computedClassId = nameToClassId[className] + if (computedClassId != null) { + return computedClassId + } + + val packageName = FqName(classInfo.packageName.replace('/', '.')) + val classId = when (classInfo) { + is TopLevelClass -> { + ClassId(packageName, FqName(classInfo.simpleName), /* local */ false) + } + is NestedNonLocalClass -> { + val outerClassId = getOrCreateClassId(classInfo.outerName) + val relativeClassName = FqName(outerClassId.relativeClassName.asString() + "." + classInfo.simpleName) + // For ClassId, a nested non-local class of a local class is also considered local (see ClassId's kdoc). + val isLocal = outerClassId.isLocal + + ClassId(packageName, relativeClassName, /* local */ isLocal) + } + is LocalClass -> { + // Note: The following computation for the relative class name of a local class does not exactly match the description given + // in ClassId's kdoc, which currently says "In the case of a local class, relativeClassName consists of a single name + // including all callables' and class' names all the way up to the package, separated by dollar signs." + // + // For example, consider this Java source: + // package com.example; + // class Foo { + // void someMethod() { + // class Bar { + // } + // } + // } + // The above class will compile into class "com/example/Foo" and "com/example/Foo$1Bar" (or + // "com/example/Foo$SomeOtherArbitraryUniqueName which need not contain the string "Bar"). + // + // Given that class, the difference between the computed ClassId and the expected ClassId is as follows: + // Value computed below Expected value as defined in ClassId's kdoc + // relativeClassName Foo$1Bar Foo$someMethod$Bar + // classId com.example.Foo$1Bar com.example.Foo$someMethod$Bar + // + // Note: While they don't match, the ClassId computed below is still unique, so it can still be used as an identifier. + // + // TODO: Compute ClassID to match the expected value. It will require collecting information about the enclosing class, + // enclosing method, and simple class name (as written in source code). There will still be a challenge if the class is an + // anonymous class: The simple class name is not available, and it's not clear what the expected ClassID is. + // + // Alternatively, check if we can safely adjust the definition of ClassId for a local class and update any related code + // accordingly. + val relativeClassName = FqName(classInfo.name.substringAfterLast('/')) + ClassId(packageName, relativeClassName, /* local */ true) + } + } + + return classId.also { + nameToClassId[className] = it + } + } + + return javaClassNames.map { getOrCreateClassId(it.name) } +} 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 211b7c55b01..c6eaf7aaa7d 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/storage/externalizers.kt @@ -187,7 +187,6 @@ object StringExternalizer : DataExternalizer { override fun read(input: DataInput): String = IOUtil.readString(input) } - // Should be consistent with org.jetbrains.jps.incremental.storage.PathStringDescriptor for correct work of portable caches object PathStringDescriptor : EnumeratorStringDescriptor() { private const val PORTABLE_CACHES_PROPERTY = "org.jetbrains.jps.portable.caches" @@ -239,6 +238,37 @@ fun DataOutput.writeString(value: String) = StringExternalizer.save(this, value) fun DataInput.readString(): String = StringExternalizer.read(this) +class NullableValueExternalizer(private val valueExternalizer: DataExternalizer) : DataExternalizer { + + override fun save(output: DataOutput, value: T?) { + output.writeBoolean(value != null) + value?.let { + valueExternalizer.save(output, it) + } + } + + override fun read(input: DataInput): T? { + return if (input.readBoolean()) { + valueExternalizer.read(input) + } else null + } +} + +object ByteArrayExternalizer : DataExternalizer { + + override fun save(output: DataOutput, bytes: ByteArray) { + output.writeInt(bytes.size) + output.write(bytes) + } + + override fun read(input: DataInput): ByteArray { + val size = input.readInt() + return ByteArray(size).also { + input.readFully(it, 0, size) + } + } +} + class ListExternalizer( private val elementExternalizer: DataExternalizer ) : DataExternalizer> { @@ -284,19 +314,3 @@ class LinkedHashMapExternalizer( return map } } - -class NullableValueExternalizer(private val valueExternalizer: DataExternalizer) : DataExternalizer { - - override fun save(output: DataOutput, value: T?) { - output.writeBoolean(value != null) - value?.let { - valueExternalizer.save(output, it) - } - } - - override fun read(input: DataInput): T? { - return if (input.readBoolean()) { - valueExternalizer.read(input) - } else null - } -} diff --git a/build-common/test/org/jetbrains/kotlin/incremental/JavaClassNameTest.kt b/build-common/test/org/jetbrains/kotlin/incremental/JavaClassNameTest.kt new file mode 100644 index 00000000000..2f90d1213cc --- /dev/null +++ b/build-common/test/org/jetbrains/kotlin/incremental/JavaClassNameTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2010-2021 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 org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File +import javax.tools.ToolProvider +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class JavaClassNameTest { + + @get:Rule + val tmpDir = TemporaryFolder() + + @Test + fun `test compute JavaClassName`() { + val compiledClasses = compileJava(className, sourceCode) + val classNames = compiledClasses.map { JavaClassName.compute(it) } + + assertEquals( + listOf( + "TopLevelClass: com/example/JavaClassWithNestedClasses", + "LocalClass: com/example/JavaClassWithNestedClasses\$1", + "LocalClass: com/example/JavaClassWithNestedClasses\$1LocalClass", + "NestedNonLocalClass: com/example/JavaClassWithNestedClasses\$1LocalClass\$InnerClassWithinLocalClass", + "LocalClass: com/example/JavaClassWithNestedClasses\$2", + "NestedNonLocalClass: com/example/JavaClassWithNestedClasses\$InnerClass", + "LocalClass: com/example/JavaClassWithNestedClasses\$InnerClass\$1LocalClassWithinInnerClass", + "NestedNonLocalClass: com/example/JavaClassWithNestedClasses\$InnerClass\$InnerClassWithinInnerClass", + "NestedNonLocalClass: com/example/JavaClassWithNestedClasses\$InnerClassWith\$Sign", + "NestedNonLocalClass: com/example/JavaClassWithNestedClasses\$StaticNestedClass" + ), + classNames.map { "${it.javaClass.simpleName}: ${it.name}" } + ) + } + + @Test + fun `test computeJavaClassIds`() { + val compiledClasses = compileJava(className, sourceCode) + val classNames = compiledClasses.map { JavaClassName.compute(it) } + val classIds = computeJavaClassIds(classNames) + + assertEquals( + listOf( + classId("com.example", "JavaClassWithNestedClasses", local = false), + classId("com.example", "JavaClassWithNestedClasses$1", local = true), + classId("com.example", "JavaClassWithNestedClasses$1LocalClass", local = true), + classId("com.example", "JavaClassWithNestedClasses$1LocalClass.InnerClassWithinLocalClass", local = true), + classId("com.example", "JavaClassWithNestedClasses$2", local = true), + classId("com.example", "JavaClassWithNestedClasses.InnerClass", local = false), + classId("com.example", "JavaClassWithNestedClasses\$InnerClass\$1LocalClassWithinInnerClass", local = true), + classId("com.example", "JavaClassWithNestedClasses.InnerClass.InnerClassWithinInnerClass", local = false), + classId("com.example", "JavaClassWithNestedClasses.InnerClassWith\$Sign", local = false), + classId("com.example", "JavaClassWithNestedClasses.StaticNestedClass", local = false) + ), + classIds + ) + } + + @Suppress("SameParameterValue") + private fun compileJava(className: String, sourceCode: String): List { + val sourceFile = File(tmpDir.newFolder(), "$className.java").apply { + parentFile.mkdirs() + writeText(sourceCode) + } + val classesDir = tmpDir.newFolder() + + val compiler = ToolProvider.getSystemJavaCompiler() + compiler.getStandardFileManager(null, null, null).use { fileManager -> + val compilationTask = compiler.getTask( + null, fileManager, null, + listOf("-d", classesDir.path), null, + fileManager.getJavaFileObjectsFromFiles(listOf(sourceFile)) + ) + assertTrue(compilationTask.call(), "Failed to compile '$className'") + } + + return classesDir.walk().filter { it.isFile } + .sortedBy { it.path.substringBefore(".class") } + .map { it.readBytes() } + .toList() + } + + private fun classId(@Suppress("SameParameterValue") packageFqName: String, relativeClassName: String, local: Boolean) = + ClassId(FqName(packageFqName), FqName(relativeClassName), local) +} + +private const val className = "com/example/JavaClassWithNestedClasses" +private val sourceCode = """ +package com.example; + +public class JavaClassWithNestedClasses { + + public class InnerClass { + + public void publicMethod() { + System.out.println("I'm in a public method"); + } + + private void privateMethod() { + System.out.println("I'm in a private method"); + } + + public class InnerClassWithinInnerClass { + } + + public void someMethod() { + + class LocalClassWithinInnerClass { + } + } + } + + public static class StaticNestedClass { + } + + public void someMethod() { + + class LocalClass { + + class InnerClassWithinLocalClass { + } + } + + Runnable objectOfAnonymousLocalClass = new Runnable() { + @Override + public void run() { + } + }; + } + + private Runnable objectOfAnonymousNonLocalClass = new Runnable() { + @Override + public void run() { + } + }; + + public class InnerClassWith${'$'}Sign { + } +} +""".trimIndent() diff --git a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt index af5f21e3a23..e8dcebf51e7 100644 --- a/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt +++ b/compiler/resolution.common.jvm/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt @@ -32,13 +32,15 @@ import java.text.CharacterIterator import java.text.StringCharacterIterator class BinaryJavaClass( - override val virtualFile: VirtualFile, + /** If virtualFile is not available, provide classContent & innerClassFinder below. */ + override val virtualFile: VirtualFile? = null, override val fqName: FqName, internal val context: ClassifierResolutionContext, private val signatureParser: BinaryClassSignatureParser, override var access: Int = 0, override val outerClass: JavaClass?, - classContent: ByteArray? = null + classContent: ByteArray? = null, + private val innerClassFinder: ((Name) -> JavaClass?)? = null ) : ClassVisitor(ASM_API_VERSION_FOR_CLASS_READING), VirtualFileBoundJavaClass, BinaryJavaModifierListOwner, MutableJavaAnnotationOwner { override val annotations: MutableCollection = SmartList() @@ -111,13 +113,17 @@ class BinaryJavaClass( } init { + if (virtualFile == null) { + checkNotNull(classContent) { "classContent must be provided when virtualFile is not available" } + checkNotNull(innerClassFinder) { "innerClassFinder must be provided when virtualFile is not available" } + } try { - ClassReader(classContent ?: virtualFile.contentsToByteArray()).accept( + ClassReader(classContent ?: virtualFile!!.contentsToByteArray()).accept( this, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES ) } catch (e: Throwable) { - throw IllegalStateException("Could not read class: $virtualFile", e) + throw IllegalStateException("Could not read class " + (virtualFile ?: ""), e) } } @@ -247,11 +253,15 @@ class BinaryJavaClass( fun findInnerClass(name: Name, classFileContent: ByteArray?): JavaClass? { val access = ownInnerClassNameToAccess[name] ?: return null - return virtualFile.parent.findChild("${virtualFile.nameWithoutExtension}$$name.class")?.let { - BinaryJavaClass( - it, fqName.child(name), context.copyForMember(), signatureParser, access, this, - classFileContent - ) + return if (virtualFile != null) { + virtualFile.parent.findChild("${virtualFile.nameWithoutExtension}$$name.class")?.let { + BinaryJavaClass( + it, fqName.child(name), context.copyForMember(), signatureParser, access, this, + classFileContent + ) + } + } else { + innerClassFinder!!(name) } } diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializationComponentsForJava.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializationComponentsForJava.kt index 2c5b555134e..226e0cd5d8a 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializationComponentsForJava.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/kotlin/DeserializationComponentsForJava.kt @@ -96,7 +96,7 @@ class DeserializationComponentsForJava( errorReporter: ErrorReporter, javaSourceElementFactory: JavaSourceElementFactory ): ModuleData { - val storageManager = LockBasedStorageManager("RuntimeModuleData") + val storageManager = LockBasedStorageManager("DeserializationComponentsForJava.ModuleData") val builtIns = JvmBuiltIns(storageManager, JvmBuiltIns.Kind.FROM_DEPENDENCIES) val module = ModuleDescriptorImpl(Name.special("<$moduleName>"), storageManager, builtIns) builtIns.builtInsModule = module diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClassFile.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClassFile.kt new file mode 100644 index 00000000000..4d6d5fb3fe7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClassFile.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2021 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.gradle.incremental + +import java.io.File + +/** Information to locate a .class file. */ +class ClassFile( + + /** Directory or jar containing the .class file. */ + val classRoot: File, + + /** + * The relative path from [classRoot] to the .class file. + * + * Any '\' characters in the path will be replaced with '/' to create [unixStyleRelativePath]. + */ + relativePath: String +) { + + /** The Unix-style relative path (with '/' as separators) from [classRoot] to the .class file. */ + val unixStyleRelativePath: String + + init { + unixStyleRelativePath = relativePath.replace('\\', '/') + } +} + +/** Information to locate a .class file, plus their contents. */ +class ClassFileWithContents( + val classFile: ClassFile, + val contents: ByteArray +) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt index 3f7fc32f70f..a132a10aae6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputer.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.gradle.incremental +import com.google.common.annotations.VisibleForTesting import com.intellij.openapi.util.io.FileUtil -import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.build.report.BuildReporter import org.jetbrains.kotlin.build.report.ICReporter import org.jetbrains.kotlin.build.report.metrics.* @@ -66,39 +66,90 @@ object ClasspathChangesComputer { for (key in current.classSnapshots.keys) { val currentSnapshot = current.classSnapshots[key]!! val previousSnapshot = previous.classSnapshots[key] ?: return Failure.AddedRemovedClasses - if (currentSnapshot !is KotlinClassSnapshot || previousSnapshot !is KotlinClassSnapshot) { - return Failure.NotYetImplemented + val result = collectClassChanges(currentSnapshot, previousSnapshot, changesCollector) + if (result !is Success) { + return result } - // TODO: Store results in changesCollector - collectKotlinClassChanges(currentSnapshot, previousSnapshot) } return Success } - @TestOnly - internal fun collectKotlinClassChanges(current: KotlinClassSnapshot, previous: KotlinClassSnapshot): DirtyData { + private fun collectClassChanges( + current: ClassSnapshot, + previous: ClassSnapshot, + @Suppress("UNUSED_PARAMETER") changesCollector: ChangesCollector + ): ChangesCollectorResult { + if (current is JavaClassSnapshot && previous is JavaClassSnapshot && + (current !is RegularJavaClassSnapshot || previous !is RegularJavaClassSnapshot) + ) { + return Failure.NotYetImplemented + } + // TODO: Store results in changesCollector and return SUCCESS here + computeClassChanges(current, previous) + return Failure.NotYetImplemented + } + + @VisibleForTesting + internal fun computeClassChanges(current: ClassSnapshot, previous: ClassSnapshot): DirtyData { // TODO Create IncrementalJvmCache early once and reuse it here val workingDir = FileUtil.createTempDirectory(this::class.java.simpleName, "_WorkingDir_${UUID.randomUUID()}", /* deleteOnExit */ true) val incrementalJvmCache = IncrementalJvmCache(workingDir, /* targetOutputDir */ null, FileToCanonicalPathConverter) + val changesCollector = ChangesCollector() + when { + current is KotlinClassSnapshot && previous is KotlinClassSnapshot -> + collectKotlinClassChanges(current, previous, incrementalJvmCache, changesCollector) + current is JavaClassSnapshot && previous is JavaClassSnapshot -> + collectJavaClassChanges(current, previous, incrementalJvmCache, changesCollector) + else -> { + // TODO: Handle current is KotlinClassSnapshot && previous is JavaClassSnapshot, and vice versa + error("Incompatible types: ${current.javaClass.name} vs. ${previous.javaClass.name}") + } + } + + workingDir.deleteRecursively() + return changesCollector.getDirtyData(listOf(incrementalJvmCache), NoOpBuildReporter.NoOpICReporter) + } + + private fun collectKotlinClassChanges( + current: KotlinClassSnapshot, + previous: KotlinClassSnapshot, + incrementalJvmCache: IncrementalJvmCache, + changesCollector: ChangesCollector + ) { // Store previous snapshot in incrementalJvmCache, the returned ChangesCollector result is not used. incrementalJvmCache.saveClassToCache( kotlinClassInfo = previous.classInfo, sourceFiles = null, changesCollector = ChangesCollector() ) + incrementalJvmCache.clearCacheForRemovedClasses(changesCollector) // Compute changes between the current snapshot and the previously stored snapshot, and store the result in changesCollector. - val changesCollector = ChangesCollector() incrementalJvmCache.saveClassToCache( kotlinClassInfo = current.classInfo, sourceFiles = null, changesCollector = changesCollector ) + incrementalJvmCache.clearCacheForRemovedClasses(changesCollector) + } - workingDir.deleteRecursively() - return changesCollector.getDirtyData(listOf(incrementalJvmCache), NoOpBuildReporter.NoOpICReporter) + private fun collectJavaClassChanges( + current: JavaClassSnapshot, + previous: JavaClassSnapshot, + incrementalJvmCache: IncrementalJvmCache, + changesCollector: ChangesCollector + ) { + // Store previous snapshot in incrementalJvmCache, the returned ChangesCollector result is not used. + val previousSnapshot = (previous as RegularJavaClassSnapshot).serializedJavaClass // TODO Handle unsafe cast + incrementalJvmCache.saveJavaClassProto(/* source */ null, previousSnapshot, ChangesCollector()) + incrementalJvmCache.clearCacheForRemovedClasses(changesCollector) + + // Compute changes between the current snapshot and the previously stored snapshot, and store the result in changesCollector. + val currentSnapshot = (current as RegularJavaClassSnapshot).serializedJavaClass + incrementalJvmCache.saveJavaClassProto(/* source */ null, currentSnapshot, changesCollector) + incrementalJvmCache.clearCacheForRemovedClasses(changesCollector) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshot.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshot.kt index 403f79993f5..e09da86c3dd 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshot.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshot.kt @@ -6,16 +6,22 @@ package org.jetbrains.kotlin.gradle.incremental import org.jetbrains.kotlin.incremental.KotlinClassInfo +import org.jetbrains.kotlin.incremental.SerializedJavaClass /** Snapshot of a classpath. It consists of a list of [ClasspathEntrySnapshot]s. */ class ClasspathSnapshot(val classpathEntrySnapshots: List) -/** Snapshot of a classpath entry (directory or jar). It consists of a list of [ClassSnapshot]s. */ +/** + * Snapshot of a classpath entry (directory or jar). It consists of a list of [ClassSnapshot]s. + * + * NOTE: It's important that the path to the classpath entry is not part of this snapshot. The reason is that classpath entries produced by + * different builds or on different machines but having the same contents should be considered the same for better build performance. + */ class ClasspathEntrySnapshot( /** - * Maps (Unix-like) relative paths of classes to their snapshots. The paths are relative to the containing classpath entry (directory or - * jar). + * Maps (Unix-style) relative paths of classes to their snapshots. The paths are relative to the containing classpath entry (directory + * or jar). */ val classSnapshots: LinkedHashMap ) @@ -34,4 +40,17 @@ sealed class ClassSnapshot class KotlinClassSnapshot(val classInfo: KotlinClassInfo) : ClassSnapshot() /** [ClassSnapshot] of a Java class. */ -object JavaClassSnapshot : ClassSnapshot() +sealed class JavaClassSnapshot : ClassSnapshot() + +/** [JavaClassSnapshot] of a typical Java class. */ +class RegularJavaClassSnapshot( + val serializedJavaClass: SerializedJavaClass +) : JavaClassSnapshot() + +/** + * [JavaClassSnapshot] of a Java class where there is nothing to capture. + * + * For example, the snapshot of a local class is empty as a local class can't be referenced from other source files and therefore any + * changes in a local class will not cause recompilation of other source files. + */ +object EmptyJavaClassSnapshot : JavaClassSnapshot() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializer.kt index 531473cf002..4c68908c212 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializer.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.incremental import com.intellij.util.io.DataExternalizer +import org.jetbrains.kotlin.incremental.JavaClassProtoMapValueExternalizer import org.jetbrains.kotlin.incremental.KotlinClassInfo import org.jetbrains.kotlin.incremental.storage.* import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader @@ -39,7 +40,7 @@ object ClasspathEntrySnapshotSerializer : DataSerializer object ClassSnapshotDataSerializer : DataSerializer { override fun save(output: DataOutput, snapshot: ClassSnapshot) { - output.writeString(snapshot.javaClass.name) + output.writeBoolean(snapshot is KotlinClassSnapshot) when (snapshot) { is KotlinClassSnapshot -> KotlinClassSnapshotExternalizer.save(output, snapshot) is JavaClassSnapshot -> JavaClassSnapshotExternalizer.save(output, snapshot) @@ -47,10 +48,11 @@ object ClassSnapshotDataSerializer : DataSerializer { } override fun read(input: DataInput): ClassSnapshot { - return when (val className = input.readString()) { - KotlinClassSnapshot::class.java.name -> KotlinClassSnapshotExternalizer.read(input) - JavaClassSnapshot::class.java.name -> JavaClassSnapshotExternalizer.read(input) - else -> error("Unrecognized class: $className") + val isKotlinClassSnapshot = input.readBoolean() + return if (isKotlinClassSnapshot) { + KotlinClassSnapshotExternalizer.read(input) + } else { + JavaClassSnapshotExternalizer.read(input) } } } @@ -122,10 +124,42 @@ object FqNameExternalizer : DataExternalizer { object JavaClassSnapshotExternalizer : DataExternalizer { override fun save(output: DataOutput, snapshot: JavaClassSnapshot) { + output.writeBoolean(snapshot is RegularJavaClassSnapshot) + when (snapshot) { + is RegularJavaClassSnapshot -> RegularJavaClassSnapshotExternalizer.save(output, snapshot) + is EmptyJavaClassSnapshot -> EmptyJavaClassSnapshotExternalizer.save(output, snapshot) + } } override fun read(input: DataInput): JavaClassSnapshot { - return JavaClassSnapshot + val isPlainJavaClassSnapshot = input.readBoolean() + return if (isPlainJavaClassSnapshot) { + RegularJavaClassSnapshotExternalizer.read(input) + } else { + EmptyJavaClassSnapshotExternalizer.read(input) + } + } +} + +object RegularJavaClassSnapshotExternalizer : DataExternalizer { + + override fun save(output: DataOutput, snapshot: RegularJavaClassSnapshot) { + JavaClassProtoMapValueExternalizer.save(output, snapshot.serializedJavaClass) + } + + override fun read(input: DataInput): RegularJavaClassSnapshot { + return RegularJavaClassSnapshot(serializedJavaClass = JavaClassProtoMapValueExternalizer.read(input)) + } +} + +object EmptyJavaClassSnapshotExternalizer : DataExternalizer { + + override fun save(output: DataOutput, snapshot: EmptyJavaClassSnapshot) { + // Nothing to save + } + + override fun read(input: DataInput): EmptyJavaClassSnapshot { + return EmptyJavaClassSnapshot } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt index 40e2258eb89..b8bf33a4d67 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotter.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.gradle.incremental -import org.jetbrains.kotlin.gradle.incremental.ClasspathEntryContentsReader.Companion.DEFAULT_CLASS_FILTER -import org.jetbrains.kotlin.incremental.KotlinClassInfo +import org.jetbrains.kotlin.incremental.* +import org.jetbrains.kotlin.name.ClassId import java.io.File import java.util.zip.ZipInputStream @@ -14,102 +14,158 @@ import java.util.zip.ZipInputStream @Suppress("SpellCheckingInspection") object ClasspathEntrySnapshotter { + private val DEFAULT_CLASS_FILTER = { unixStyleRelativePath: String, isDirectory: Boolean -> + !isDirectory + && unixStyleRelativePath.endsWith(".class", ignoreCase = true) + && !unixStyleRelativePath.endsWith("module-info.class", ignoreCase = true) + && !unixStyleRelativePath.startsWith("meta-inf", ignoreCase = true) + } + fun snapshot(classpathEntry: File): ClasspathEntrySnapshot { - val pathsToContents: LinkedHashMap = - ClasspathEntryContentsReader.from(classpathEntry).readContents(DEFAULT_CLASS_FILTER) + val classes = + DirectoryOrJarContentsReader.read(classpathEntry, DEFAULT_CLASS_FILTER) + .map { (unixStyleRelativePath, contents) -> + ClassFileWithContents(ClassFile(classpathEntry, unixStyleRelativePath), contents) + } - val pathsToSnapshots = LinkedHashMap() - pathsToContents.mapValuesTo(pathsToSnapshots) { (_, classContents) -> - ClassSnapshotter.snapshot(classContents) - } + val snapshots = ClassSnapshotter.snapshot(classes) - return ClasspathEntrySnapshot(pathsToSnapshots) + val relativePathsToSnapshotsMap = + classes.map { it.classFile.unixStyleRelativePath }.zip(snapshots).toMap(LinkedHashMap()) + return ClasspathEntrySnapshot(relativePathsToSnapshotsMap) } } -/** Computes a [ClassSnapshot] of a class. */ +/** Creates [ClassSnapshot]s of classes. */ @Suppress("SpellCheckingInspection") object ClassSnapshotter { - fun snapshot(classContents: ByteArray): ClassSnapshot { - return KotlinClassInfo.tryCreateFrom(classContents)?.let { KotlinClassSnapshot(it) } - ?: JavaClassSnapshot - } -} - -/** Utility to read the contents of a classpath entry (directory or jar). */ -sealed class ClasspathEntryContentsReader { - - companion object { - - val DEFAULT_CLASS_FILTER = { invariantSeparatorsRelativePath: String, isDirectory: Boolean -> - !isDirectory - && invariantSeparatorsRelativePath.endsWith(".class", ignoreCase = true) - && !invariantSeparatorsRelativePath.endsWith("module-info.class", ignoreCase = true) - && !invariantSeparatorsRelativePath.startsWith("meta-inf", ignoreCase = true) + /** + * Creates [ClassSnapshot]s of the given classes. + * + * Note that for Java (non-Kotlin) classes, creating a [ClassSnapshot] for a nested class will require accessing the outer class (and + * possibly vice versa). Therefore, outer classes and nested classes must be passed together in one invocation of this method. + */ + fun snapshot(classes: List): List { + // Snapshot Kotlin classes first + val kotlinClassSnapshots: Map = classes.associate { + it.classFile to trySnapshotKotlinClass(it) } - /** Creates a [ClasspathEntryContentsReader] for the given classpath entry (directory or jar). */ - fun from(classpathEntry: File): ClasspathEntryContentsReader { - return if (classpathEntry.isDirectory) { - DirectoryContentsReader(classpathEntry) - } else { - JarContentsReader(classpathEntry) - } + // Snapshot Java classes in one invocation + val javaClasses: List = classes.filter { kotlinClassSnapshots[it.classFile] == null } + val snapshots: List = snapshotJavaClasses(javaClasses) + val javaClassSnapshots: Map = javaClasses.map { it.classFile }.zip(snapshots).toMap() + + // Return a snapshot for each class + return classes.map { kotlinClassSnapshots[it.classFile] ?: javaClassSnapshots[it.classFile]!! } + } + + /** Creates [KotlinClassSnapshot] of the given class, or returns `null` if the class is not a Kotlin class. */ + private fun trySnapshotKotlinClass(clazz: ClassFileWithContents): KotlinClassSnapshot? { + return KotlinClassInfo.tryCreateFrom(clazz.contents)?.let { + KotlinClassSnapshot(it) } } /** - * Returns a map from (Unix-like) relative paths of classes to their contents. The paths are relative to the containing classpath entry - * (directory or jar). + * Creates [JavaClassSnapshot]s of the given Java classes. + * + * Note that creating a [JavaClassSnapshot] for a nested class will require accessing the outer class (and possibly vice versa). + * Therefore, outer classes and nested classes must be passed together in one invocation of this method. + */ + private fun snapshotJavaClasses(classes: List): List { + val classFiles = classes.map { it.classFile } + val classesContents = classes.map { it.contents } + val classNames = classesContents.map { JavaClassName.compute(it) } + val classIds = computeJavaClassIds(classNames) + + // Snapshot special cases first + // Map a class index to its snapshot, or `null` if it will be created later + val specialCaseSnapshots: Map = classFiles.indices.associateWith { index -> + val className = classNames[index] + val classId = classIds[index] + if (classId.isLocal) { + // A local class can't be referenced from other source files, so any changes in a local class will not cause recompilation + // of other source files. Therefore, the snapshot of a local class is empty. + // In that regard, a nested class of a local class is also considered local (which matches the definition of + // ClassId.isLocal, see ClassId's kdoc). Therefore, we checked `classId.isLocal`, which is a super set of `className is + // LocalClass`. + EmptyJavaClassSnapshot + } else if (className is NestedNonLocalClass && (className.isAnonymous || className.isSynthetic)) { + // An anonymous or synthetic class also can't be referenced from other source files, so its snapshot is also empty. + EmptyJavaClassSnapshot + } else { + null + } + } + + // Snapshot the remaining classes in one invocation + val remainingClassesIndices: List = classFiles.indices.filter { specialCaseSnapshots[it] == null } + val remainingClassIds: List = remainingClassesIndices.map { classIds[it] } + val remainingClassesContents: List = remainingClassesIndices.map { classes[it].contents } + + val snapshots: List = JavaClassDescriptorCreator.create(remainingClassIds, remainingClassesContents).map { + RegularJavaClassSnapshot(it.toSerializedJavaClass()) + } + val remainingSnapshots: Map /* maps a class index to its snapshot */ = + remainingClassesIndices.zip(snapshots).toMap() + + // Return a snapshot for each class + return classFiles.indices.map { specialCaseSnapshots[it] ?: remainingSnapshots[it]!! } + } +} + +/** Utility to read the contents of a directory or jar. */ +private object DirectoryOrJarContentsReader { + + /** + * Returns a map from Unix-style relative paths of entries to their contents. The paths are relative to the container (directory or + * jar). * * The map entries need to satisfy the given filter. * - * The map entries are sorted based on their (Unix-like) relative paths (to ensure deterministic results across filesystems). + * The map entries are sorted based on their Unix-style relative paths (to ensure deterministic results across filesystems). * * Note: If a jar has duplicate entries, only one of them will be used (there is no guarantee which one will be used). */ - abstract fun readContents(filter: ((invariantSeparatorsRelativePath: String, isDirectory: Boolean) -> Boolean)? = null): - LinkedHashMap -} - -/** Utility to read the contents of a directory. */ -class DirectoryContentsReader(private val directory: File) : ClasspathEntryContentsReader() { - - init { - check(directory.isDirectory) + fun read( + directoryOrJar: File, + entryFilter: ((unixStyleRelativePath: String, isDirectory: Boolean) -> Boolean)? = null + ): LinkedHashMap { + return if (directoryOrJar.isDirectory) { + readDirectory(directoryOrJar, entryFilter) + } else { + check(directoryOrJar.isFile && directoryOrJar.path.endsWith(".jar", ignoreCase = true)) + readJar(directoryOrJar, entryFilter) + } } - override fun readContents( - filter: ((invariantSeparatorsRelativePath: String, isDirectory: Boolean) -> Boolean)? + private fun readDirectory( + directory: File, + entryFilter: ((unixStyleRelativePath: String, isDirectory: Boolean) -> Boolean)? = null ): LinkedHashMap { val relativePathsToContents: MutableList> = mutableListOf() - directory.walk().forEach { - val invariantSeparatorsRelativePath = it.relativeTo(directory).invariantSeparatorsPath - if (filter == null || filter(invariantSeparatorsRelativePath, it.isDirectory)) { - relativePathsToContents.add(invariantSeparatorsRelativePath to it.readBytes()) + directory.walk().forEach { file -> + val unixStyleRelativePath = file.relativeTo(directory).invariantSeparatorsPath + if (entryFilter == null || entryFilter(unixStyleRelativePath, file.isDirectory)) { + relativePathsToContents.add(unixStyleRelativePath to file.readBytes()) } } return relativePathsToContents.sortedBy { it.first }.toMap(LinkedHashMap()) } -} -/** Utility to read the contents of a jar. */ -class JarContentsReader(private val jarFile: File) : ClasspathEntryContentsReader() { - - init { - check(jarFile.path.endsWith(".jar", ignoreCase = true)) - } - - override fun readContents( - filter: ((invariantSeparatorsRelativePath: String, isDirectory: Boolean) -> Boolean)? + private fun readJar( + jarFile: File, + entryFilter: ((unixStyleRelativePath: String, isDirectory: Boolean) -> Boolean)? = null ): LinkedHashMap { val relativePathsToContents: MutableList> = mutableListOf() ZipInputStream(jarFile.inputStream().buffered()).use { zipInputStream -> while (true) { val entry = zipInputStream.nextEntry ?: break - if (filter == null || filter(entry.name, entry.isDirectory)) { - relativePathsToContents.add(entry.name to zipInputStream.readBytes()) + val unixStyleRelativePath = entry.name + if (entryFilter == null || entryFilter(unixStyleRelativePath, entry.isDirectory)) { + relativePathsToContents.add(unixStyleRelativePath to zipInputStream.readBytes()) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt index 24b2846f050..e3c7581d115 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/KaptGenerateStubsTask.kt @@ -46,7 +46,7 @@ abstract class KaptGenerateStubsTask : KotlinCompile(KotlinJvmOptionsImpl()) { ) : KotlinCompile.Configurator(kotlinCompilation, properties) { override fun getClasspathSnapshotDir(task: KaptGenerateStubsTask): Provider = - task.project.objects.directoryProperty().dir(classpathSnapshotDir.path) + task.project.objects.directoryProperty().fileValue(classpathSnapshotDir) override fun configure(task: KaptGenerateStubsTask) { super.configure(task) diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt index fe293e44051..e2b57356815 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathChangesComputerTest.kt @@ -33,19 +33,17 @@ abstract class ClasspathChangesComputerTest : ClasspathSnapshotTestCommon() { @Test fun testCollectClassChanges_changedPublicMethodSignature() { val updatedSnapshot = testSourceFile.changePublicMethodSignature().compileAndSnapshot() - val dirtyData = ClasspathChangesComputer.collectKotlinClassChanges( - updatedSnapshot as KotlinClassSnapshot, - originalSnapshot as KotlinClassSnapshot - ) + val dirtyData = ClasspathChangesComputer.computeClassChanges(updatedSnapshot, originalSnapshot) + val testClass = testSourceFile.sourceFile.unixStyleRelativePath.substringBeforeLast('.').replace('/', '.') assertEquals( DirtyData( dirtyLookupSymbols = setOf( - LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = "com.example.SimpleKotlinClass"), - LookupSymbol(name = "publicMethod", scope = "com.example.SimpleKotlinClass"), - LookupSymbol(name = "changedPublicMethod", scope = "com.example.SimpleKotlinClass") + LookupSymbol(name = SAM_LOOKUP_NAME.asString(), scope = testClass), + LookupSymbol(name = "publicMethod", scope = testClass), + LookupSymbol(name = "changedPublicMethod", scope = testClass) ), - dirtyClassesFqNames = setOf(FqName("com.example.SimpleKotlinClass")), + dirtyClassesFqNames = setOf(FqName(testClass)), dirtyClassesFqNamesForceRecompile = emptySet() ), dirtyData @@ -55,10 +53,7 @@ abstract class ClasspathChangesComputerTest : ClasspathSnapshotTestCommon() { @Test fun testCollectClassChanges_changedMethodImplementation() { val updatedSnapshot = testSourceFile.changeMethodImplementation().compileAndSnapshot() - val dirtyData = ClasspathChangesComputer.collectKotlinClassChanges( - updatedSnapshot as KotlinClassSnapshot, - originalSnapshot as KotlinClassSnapshot - ) + val dirtyData = ClasspathChangesComputer.computeClassChanges(updatedSnapshot, originalSnapshot) assertEquals(DirtyData(emptySet(), emptySet(), emptySet()), dirtyData) } @@ -67,3 +62,7 @@ abstract class ClasspathChangesComputerTest : ClasspathSnapshotTestCommon() { class KotlinClassesClasspathChangesComputerTest : ClasspathChangesComputerTest() { override val testSourceFile = SimpleKotlinClass(tmpDir) } + +class JavaClassesClasspathChangesComputerTest : ClasspathChangesComputerTest() { + override val testSourceFile = SimpleJavaClass(tmpDir) +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializerTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializerTest.kt index d2fba8f8ae1..7832cf77088 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializerTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotSerializerTest.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.gradle.incremental -import org.junit.Assert.assertEquals +import org.junit.Assert.* import org.junit.Test abstract class ClasspathSnapshotSerializerTest : ClasspathSnapshotTestCommon() { @@ -13,7 +13,7 @@ abstract class ClasspathSnapshotSerializerTest : ClasspathSnapshotTestCommon() { protected abstract val testSourceFile: ChangeableTestSourceFile @Test - fun `test ClassSnapshotDataSerializer`() { + open fun `test ClassSnapshotDataSerializer`() { val originalSnapshot = testSourceFile.compileAndSnapshot() val serializedSnapshot = ClassSnapshotDataSerializer.toByteArray(originalSnapshot) val deserializedSnapshot = ClassSnapshotDataSerializer.fromByteArray(serializedSnapshot) @@ -25,3 +25,30 @@ abstract class ClasspathSnapshotSerializerTest : ClasspathSnapshotTestCommon() { class KotlinClassesClasspathSnapshotSerializerTest : ClasspathSnapshotSerializerTest() { override val testSourceFile = SimpleKotlinClass(tmpDir) } + +class JavaClassesClasspathSnapshotSerializerTest : ClasspathSnapshotSerializerTest() { + + override val testSourceFile = SimpleJavaClass(tmpDir) + + @Test + override fun `test ClassSnapshotDataSerializer`() { + val originalSnapshot = testSourceFile.compileAndSnapshot() + val serializedSnapshot = ClassSnapshotDataSerializer.toByteArray(originalSnapshot) + val deserializedSnapshot = ClassSnapshotDataSerializer.fromByteArray(serializedSnapshot) + + // The deserialized object does not exactly match the original object as they contain fields called `memoizedSerializedSize` which + // are not part of the serialized data and their values seem to be generated separately. Therefore, we remove these fields from the + // check below. + assertNotEquals(originalSnapshot.toGson(), deserializedSnapshot.toGson()) + assertEquals( + originalSnapshot.toGson().stripLinesContainingText("memoizedSerializedSize"), + deserializedSnapshot.toGson().stripLinesContainingText("memoizedSerializedSize") + ) + // Add another check to confirm that those fields are not part of the serialized data. + assertArrayEquals(serializedSnapshot, ClassSnapshotDataSerializer.toByteArray(deserializedSnapshot)) + } + + private fun String.stripLinesContainingText(text: String): String { + return lines().filterNot { it.contains(text, ignoreCase = true) }.joinToString("\n") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt index 7928f786d93..0edf1a19c95 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotTestCommon.kt @@ -6,6 +6,9 @@ package org.jetbrains.kotlin.gradle.incremental import com.google.gson.GsonBuilder +import org.jetbrains.kotlin.gradle.incremental.ClasspathSnapshotTestCommon.Util.readBytes +import org.jetbrains.kotlin.gradle.incremental.ClasspathSnapshotTestCommon.Util.snapshot +import org.jetbrains.kotlin.gradle.util.compileSources import org.junit.Rule import org.junit.rules.TemporaryFolder import java.io.File @@ -19,51 +22,99 @@ abstract class ClasspathSnapshotTestCommon { @get:Rule val tmpDir = TemporaryFolder() - abstract class RelativeFile(val baseDir: File, val relativePath: String) { - fun asFile() = File(baseDir, relativePath) - } + // Use Gson to compare objects + private val gson by lazy { GsonBuilder().setPrettyPrinting().create() } + protected fun Any.toGson(): String = gson.toJson(this) - class SourceFile(baseDir: File, relativePath: String) : RelativeFile(baseDir, relativePath) { - init { - check(relativePath.endsWith(".kt") || relativePath.endsWith(".java")) - } - } + class SourceFile(val baseDir: File, relativePath: String) { + val unixStyleRelativePath: String - class ClassFile(baseDir: File, relativePath: String) : RelativeFile(baseDir, relativePath) { init { - check(relativePath.endsWith(".class")) + unixStyleRelativePath = relativePath.replace('\\', '/') } - fun snapshot() = ClassSnapshotter.snapshot(asFile().readBytes()) + fun asFile() = File(baseDir, unixStyleRelativePath) } - open class TestSourceFile(val sourceFile: SourceFile, val tmpDir: TemporaryFolder) { + /** Same as [SourceFile] but with a [TemporaryFolder] to store the results of operations on the [SourceFile]. */ + open class TestSourceFile(val sourceFile: SourceFile, protected val tmpDir: TemporaryFolder) { - fun replace(oldValue: String, newValue: String): TestSourceFile { + fun replace(oldValue: String, newValue: String, newBaseDir: File? = null): TestSourceFile { val fileContents = sourceFile.asFile().readText() check(fileContents.contains(oldValue)) { "String '$oldValue' not found in file '${sourceFile.asFile().path}'" } - val newSourceFile = SourceFile(tmpDir.newFolder(), sourceFile.relativePath).also { it.asFile().parentFile.mkdirs() } + val newSourceFile = SourceFile(newBaseDir ?: tmpDir.newFolder(), sourceFile.unixStyleRelativePath) + newSourceFile.asFile().parentFile.mkdirs() newSourceFile.asFile().writeText(fileContents.replace(oldValue, newValue)) return TestSourceFile(newSourceFile, tmpDir) } - fun compile(): ClassFile { + /** + * Compiles this source file and returns a single generated .class file, or fails if zero or more than one .class file was + * generated. + * + * Alternatively, the caller can call [compileAll] to get all generated .class files. + */ + fun compile(): ClassFile = compileAll().single() + + /** Compiles this source file and returns all generated .class files. */ + @Suppress("MemberVisibilityCanBePrivate") + fun compileAll(): List { + val filePath = sourceFile.asFile().path return when { - sourceFile.relativePath.endsWith(".kt") -> compileKotlin() - else -> error("Unexpected file name extension: '${sourceFile.asFile().path}'") + filePath.endsWith(".kt") -> compileKotlin() + filePath.endsWith(".java") -> compileJava() + else -> error("Unexpected file name extension: $filePath") } } - private fun compileKotlin(): ClassFile { + private fun compileKotlin(): List { // TODO: Call Kotlin compiler to generate classes (see https://github.com/JetBrains/kotlin/pull/4512#discussion_r679432232) - return ClassFile( - File(sourceFile.baseDir.path.substringBeforeLast("src") + "classes" + sourceFile.baseDir.path.substringAfterLast("src")), - sourceFile.relativePath.substringBeforeLast('.') + ".class" + // Currently, we use the precompiled classes in the test data. + return listOf( + ClassFile( + File(testDataDir.path + "/classes/" + sourceFile.baseDir.name), + sourceFile.unixStyleRelativePath.replace(".kt", ".class") + ) ) } - fun compileAndSnapshot(): ClassSnapshot = compile().snapshot() + private fun compileJava(): List { + val classesDir = tmpDir.newFolder() + compileSources(listOf(sourceFile.asFile()), classesDir) + return classesDir.walk().filter { it.isFile } + .map { ClassFile(classesDir, it.toRelativeString(classesDir)) } + .sortedBy { it.unixStyleRelativePath.substringBefore(".class") } + .toList() + } + + /** + * Compiles this source file and returns the snapshot of a single generated .class file, or fails if zero or more than one .class + * file was generated. + * + * Alternatively, the caller can call [compileAndSnapshotAll] to get the snapshots of all generated .class files. + */ + fun compileAndSnapshot() = compile().snapshot() + + /** Compiles this source file and returns the snapshots of all generated .class files. */ + fun compileAndSnapshotAll(): List { + val classes = compileAll().map { + ClassFileWithContents(it, it.readBytes()) + } + return ClassSnapshotter.snapshot(classes) + } + } + + object Util { + + fun ClassFile.readBytes(): ByteArray { + // The class files in tests are currently in a directory, so we don't need to handle jars + return File(classRoot, unixStyleRelativePath).readBytes() + } + + fun ClassFile.snapshot(): ClassSnapshot { + return ClassSnapshotter.snapshot(listOf(ClassFileWithContents(this, readBytes()))).single() + } } abstract class ChangeableTestSourceFile(sourceFile: SourceFile, tmpDir: TemporaryFolder) : TestSourceFile(sourceFile, tmpDir) { @@ -77,26 +128,35 @@ abstract class ClasspathSnapshotTestCommon { SourceFile(File(testDataDir, "src/original"), "com/example/SimpleKotlinClass.kt"), tmpDir ) { - override fun changePublicMethodSignature(): TestSourceFile { - return TestSourceFile( - SourceFile( - File(sourceFile.baseDir.path.replace("original", "changedPublicMethodSignature")), - sourceFile.relativePath - ), tmpDir - ) - } + override fun changePublicMethodSignature() = replace( + "publicMethod()", "changedPublicMethod()", + newBaseDir = File(tmpDir.newFolder(), "changedPublicMethodSignature") + ) - override fun changeMethodImplementation(): TestSourceFile { - return TestSourceFile( - SourceFile( - File(sourceFile.baseDir.path.replace("original", "changedMethodImplementation")), - sourceFile.relativePath - ), tmpDir - ) - } + override fun changeMethodImplementation() = replace( + "I'm in a public method", "This method implementation has changed!", + newBaseDir = File(tmpDir.newFolder(), "changedMethodImplementation") + ) } - // Use Gson to compare objects - private val gson by lazy { GsonBuilder().setPrettyPrinting().create() } - protected fun Any.toGson(): String = gson.toJson(this) + class SimpleJavaClass(tmpDir: TemporaryFolder) : ChangeableTestSourceFile( + SourceFile(File(testDataDir, "src/original"), "com/example/SimpleJavaClass.java"), tmpDir + ) { + + override fun changePublicMethodSignature() = replace("publicMethod()", "changedPublicMethod()") + + override fun changeMethodImplementation() = replace("I'm in a public method", "This method implementation has changed!") + } + + class JavaClassWithNestedClasses(tmpDir: TemporaryFolder) : ChangeableTestSourceFile( + SourceFile(File(testDataDir, "src/original"), "com/example/JavaClassWithNestedClasses.java"), tmpDir + ) { + + /** The source file contains multiple classes, select the one we are mostly interested in. */ + val nestedClassToTest = "com/example/JavaClassWithNestedClasses\$InnerClass" + + override fun changePublicMethodSignature() = replace("publicMethod()", "changedPublicMethod()") + + override fun changeMethodImplementation() = replace("I'm in a public method", "This method implementation has changed!") + } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotterTest.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotterTest.kt index 7894d7756b1..7ff66bc1712 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotterTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/incremental/ClasspathSnapshotterTest.kt @@ -52,3 +52,47 @@ abstract class ClasspathSnapshotterTest : ClasspathSnapshotTestCommon() { class KotlinClassesClasspathSnapshotterTest : ClasspathSnapshotterTest() { override val testSourceFile = SimpleKotlinClass(tmpDir) } + +class JavaClassesClasspathSnapshotterTest : ClasspathSnapshotterTest() { + override val testSourceFile = SimpleJavaClass(tmpDir) +} + +class JavaClassWithNestedClassesClasspathSnapshotterTest : ClasspathSnapshotTestCommon() { + + private val testSourceFile = JavaClassWithNestedClasses(tmpDir) + + private lateinit var testClassSnapshot: ClassSnapshot + + @Before + fun setUp() { + testClassSnapshot = testSourceFile.compileAndSnapshotNestedClass() + } + + private fun TestSourceFile.compileAndSnapshotNestedClass(): ClassSnapshot { + return compileAndSnapshotAll()[5].also { + assertEquals( + testSourceFile.nestedClassToTest, + (it as RegularJavaClassSnapshot).serializedJavaClass.classId.asString().replace('.', '$') + ) + } + } + + @Test + fun `test ClassSnapshotter's result against expected snapshot`() { + val expectedSnapshot = + File("${testDataDir.path}/src/original/${testSourceFile.nestedClassToTest}-expected-snapshot.json").readText() + assertEquals(expectedSnapshot, testClassSnapshot.toGson()) + } + + @Test + fun `test ClassSnapshotter extracts ABI info from a class`() { + val updatedSnapshot = testSourceFile.changePublicMethodSignature().compileAndSnapshotNestedClass() + assertNotEquals(testClassSnapshot.toGson(), updatedSnapshot.toGson()) + } + + @Test + fun `test ClassSnapshotter does not extract non-ABI info from a class`() { + val updatedSnapshot = testSourceFile.changeMethodImplementation().compileAndSnapshotNestedClass() + assertEquals(testClassSnapshot.toGson(), updatedSnapshot.toGson()) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/changedMethodImplementation/com/example/SimpleKotlinClass.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/changedMethodImplementation/com/example/SimpleKotlinClass.kt deleted file mode 100644 index f2be5880a0e..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/changedMethodImplementation/com/example/SimpleKotlinClass.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.example - -class SimpleKotlinClass { - - fun publicMethod() { - println("This method implementation has changed!") - } - - private fun privateMethod() { - println("I'm in a private method") - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/changedPublicMethodSignature/com/example/SimpleKotlinClass.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/changedPublicMethodSignature/com/example/SimpleKotlinClass.kt deleted file mode 100644 index 77cb2519a22..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/changedPublicMethodSignature/com/example/SimpleKotlinClass.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.example - -class SimpleKotlinClass { - - fun changedPublicMethod() { - println("I'm in a public method") - } - - private fun privateMethod() { - println("I'm in a private method") - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/JavaClassWithNestedClasses$InnerClass-expected-snapshot.json b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/JavaClassWithNestedClasses$InnerClass-expected-snapshot.json new file mode 100644 index 00000000000..05d5f30bfbc --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/JavaClassWithNestedClasses$InnerClass-expected-snapshot.json @@ -0,0 +1,1119 @@ +{ + "serializedJavaClass": { + "proto": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 3, + "flags_": 22, + "fqName_": 3, + "companionObjectName_": 0, + "typeParameter_": [], + "supertype_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 6, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "supertypeId_": [], + "supertypeIdMemoizedSerializedSize": -1, + "nestedClassName_": [ + 13 + ], + "nestedClassNameMemoizedSerializedSize": -1, + "constructor_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 1, + "flags_": 54, + "valueParameter_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 6, + "flags_": 0, + "name_": 7, + "type_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 20, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 17, + "argument_": [], + "nullable_": true, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 2, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 2, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "typeId_": 0, + "varargElementType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "varargElementTypeId_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "versionRequirement_": [], + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "function_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 13, + "flags_": 32786, + "oldFlags_": 6, + "name_": 8, + "returnType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 8, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "returnTypeId_": 0, + "typeParameter_": [], + "receiverType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "receiverTypeId_": 0, + "valueParameter_": [], + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "contract_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "effect_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 13, + "flags_": 32790, + "oldFlags_": 6, + "name_": 11, + "returnType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 8, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "returnTypeId_": 0, + "typeParameter_": [], + "receiverType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "receiverTypeId_": 0, + "valueParameter_": [], + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "contract_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "effect_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 13, + "flags_": 32790, + "oldFlags_": 6, + "name_": 12, + "returnType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 8, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "returnTypeId_": 0, + "typeParameter_": [], + "receiverType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "receiverTypeId_": 0, + "valueParameter_": [], + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "contract_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "effect_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "property_": [], + "typeAlias_": [], + "enumEntry_": [], + "sealedSubclassFqName_": [], + "sealedSubclassFqNameMemoizedSerializedSize": -1, + "inlineClassUnderlyingPropertyName_": 0, + "inlineClassUnderlyingType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "inlineClassUnderlyingTypeId_": 0, + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "versionRequirementTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "requirement_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "stringTable": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "string_": [ + "com", + "example", + "JavaClassWithNestedClasses", + "InnerClass", + "java", + "lang", + "Object", + "p0", + "privateMethod", + "kotlin", + "Unit", + "publicMethod", + "someMethod", + "InnerClassWithinInnerClass" + ], + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "qualifiedNameTable": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "qualifiedName_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 2, + "parentQualifiedName_": -1, + "shortName_": 0, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 3, + "parentQualifiedName_": 0, + "shortName_": 1, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 1, + "shortName_": 2, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 2, + "shortName_": 3, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 2, + "parentQualifiedName_": -1, + "shortName_": 4, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 3, + "parentQualifiedName_": 4, + "shortName_": 5, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 5, + "shortName_": 6, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 2, + "parentQualifiedName_": -1, + "shortName_": 9, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 7, + "shortName_": 10, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + } + ], + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/JavaClassWithNestedClasses.java b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/JavaClassWithNestedClasses.java new file mode 100644 index 00000000000..f77d720eefc --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/JavaClassWithNestedClasses.java @@ -0,0 +1,51 @@ +package com.example; + +public class JavaClassWithNestedClasses { + + public class InnerClass { + + public void publicMethod() { + System.out.println("I'm in a public method"); + } + + private void privateMethod() { + System.out.println("I'm in a private method"); + } + + public class InnerClassWithinInnerClass { + } + + public void someMethod() { + + class LocalClassWithinInnerClass { + } + } + } + + public static class StaticNestedClass { + } + + public void someMethod() { + + class LocalClass { + + class InnerClassWithinLocalClass { + } + } + + Runnable objectOfAnonymousLocalClass = new Runnable() { + @Override + public void run() { + } + }; + } + + private Runnable objectOfAnonymousNonLocalClass = new Runnable() { + @Override + public void run() { + } + }; + + public class InnerClassWith$Sign { + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleJavaClass-expected-snapshot.json b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleJavaClass-expected-snapshot.json new file mode 100644 index 00000000000..eaa4f78a567 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleJavaClass-expected-snapshot.json @@ -0,0 +1,697 @@ +{ + "serializedJavaClass": { + "proto": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 3, + "flags_": 22, + "fqName_": 2, + "companionObjectName_": 0, + "typeParameter_": [], + "supertype_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 5, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "supertypeId_": [], + "supertypeIdMemoizedSerializedSize": -1, + "nestedClassName_": [], + "nestedClassNameMemoizedSerializedSize": -1, + "constructor_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 1, + "flags_": 54, + "valueParameter_": [], + "versionRequirement_": [], + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "function_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 13, + "flags_": 32786, + "oldFlags_": 6, + "name_": 6, + "returnType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 7, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "returnTypeId_": 0, + "typeParameter_": [], + "receiverType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "receiverTypeId_": 0, + "valueParameter_": [], + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "contract_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "effect_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 13, + "flags_": 32790, + "oldFlags_": 6, + "name_": 9, + "returnType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 16, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBound_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "flexibleUpperBoundId_": 0, + "className_": 7, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "outerTypeId_": 0, + "abbreviatedType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "returnTypeId_": 0, + "typeParameter_": [], + "receiverType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "receiverTypeId_": 0, + "valueParameter_": [], + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "contract_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "effect_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + } + ], + "property_": [], + "typeAlias_": [], + "enumEntry_": [], + "sealedSubclassFqName_": [], + "sealedSubclassFqNameMemoizedSerializedSize": -1, + "inlineClassUnderlyingPropertyName_": 0, + "inlineClassUnderlyingType_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "argument_": [], + "nullable_": false, + "flexibleTypeCapabilitiesId_": 0, + "flexibleUpperBoundId_": 0, + "className_": 0, + "typeParameter_": 0, + "typeParameterName_": 0, + "typeAliasName_": 0, + "outerTypeId_": 0, + "abbreviatedTypeId_": 0, + "flags_": 0, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": false, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "inlineClassUnderlyingTypeId_": 0, + "typeTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 0, + "type_": [], + "firstNullable_": -1, + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "versionRequirement_": [], + "versionRequirementTable_": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "requirement_": [], + "memoizedIsInitialized": -1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "extensions": { + "fields": {}, + "isImmutable": true, + "hasLazyField": false + }, + "memoizedHashCode": 0 + }, + "stringTable": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "string_": [ + "com", + "example", + "SimpleJavaClass", + "java", + "lang", + "Object", + "privateMethod", + "kotlin", + "Unit", + "publicMethod" + ], + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + "qualifiedNameTable": { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "qualifiedName_": [ + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 2, + "parentQualifiedName_": -1, + "shortName_": 0, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 3, + "parentQualifiedName_": 0, + "shortName_": 1, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 1, + "shortName_": 2, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 2, + "parentQualifiedName_": -1, + "shortName_": 3, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 3, + "parentQualifiedName_": 3, + "shortName_": 4, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 4, + "shortName_": 5, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 2, + "parentQualifiedName_": -1, + "shortName_": 7, + "kind_": "PACKAGE", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + }, + { + "unknownFields": { + "bytes": [], + "hash": 0 + }, + "bitField0_": 7, + "parentQualifiedName_": 6, + "shortName_": 8, + "kind_": "CLASS", + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + } + ], + "memoizedIsInitialized": 1, + "memoizedSerializedSize": -1, + "memoizedHashCode": 0 + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleJavaClass.java b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleJavaClass.java new file mode 100644 index 00000000000..7455941f80e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleJavaClass.java @@ -0,0 +1,12 @@ +package com.example; + +public class SimpleJavaClass { + + public void publicMethod() { + System.out.println("I'm in a public method"); + } + + private void privateMethod() { + System.out.println("I'm in a private method"); + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleKotlinClass.kt b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleKotlinClass.kt index 295d4df6993..00e00af258f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleKotlinClass.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/testData/kotlin.incremental.useClasspathSnapshot/src/original/com/example/SimpleKotlinClass.kt @@ -9,4 +9,4 @@ class SimpleKotlinClass { private fun privateMethod() { println("I'm in a private method") } -} \ No newline at end of file +}