[JS IR] Fix IC invalidation process when symbols are modified

The patch fixes the cases of IC invalidation when symbols are modified:
 adding and removing inline, data, in, out, suspend qualifiers.

 For that purpose an extra hash is used.
 It is written in a direct dependency graph into an IC cache metadata file.
 For inline functions a transitive hash is used.
 For non-inline functions, classes, and others a symbol hash is used.
 The invalidation routine marks the file as dirty
 (with 'updated imports' state) if hash is modified.

^KT-51083 Fixed
^KT-51088 Fixed
^KT-51090 Fixed
^KT-51099 Fixed
This commit is contained in:
Alexander Korepanov
2022-06-15 19:53:13 +02:00
committed by Space
parent 644447db84
commit 5b4e9e2966
62 changed files with 279 additions and 202 deletions
@@ -37,7 +37,7 @@ enum class DirtyFileState(val str: String) {
ADDED_FILE("added file"),
MODIFIED_IR("modified ir"),
UPDATED_EXPORTS("updated exports"),
UPDATED_INLINE_IMPORTS("updated inline imports"),
UPDATED_IMPORTS("updated imports"),
REMOVED_INVERSE_DEPENDS("removed inverse depends"),
REMOVED_DIRECT_DEPENDS("removed direct depends"),
REMOVED_FILE("removed file")
@@ -52,9 +52,7 @@ class CacheUpdater(
private val mainArguments: List<String>?,
private val executor: CacheExecutor
) {
private val hashCalculator = InlineFunctionTransitiveHashCalculator()
private val transitiveHashes
get() = hashCalculator.transitiveHashes
private val signatureHashCalculator = IdSignatureHashCalculator()
private val libraries = loadLibraries(allModules)
private val dependencyGraph = buildDependenciesGraph(libraries)
@@ -117,26 +115,29 @@ class CacheUpdater(
val oldDirectDependencies: KotlinSourceFileMap<*>,
override val inverseDependencies: KotlinSourceFileMutableMap<MutableSet<IdSignature>> = KotlinSourceFileMutableMap(),
override val directDependencies: KotlinSourceFileMutableMap<MutableSet<IdSignature>> = KotlinSourceFileMutableMap(),
override val importedInlineFunctions: MutableMap<IdSignature, ICHash> = mutableMapOf()
override val directDependencies: KotlinSourceFileMutableMap<MutableMap<IdSignature, ICHash>> = KotlinSourceFileMutableMap(),
) : KotlinSourceFileMetadata() {
fun addInverseDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature) =
inverseDependencies.addSignature(lib, src, signature)
when (val signatures = inverseDependencies[lib, src]) {
null -> inverseDependencies[lib, src] = mutableSetOf(signature)
else -> signatures += signature
}
fun addDirectDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature) =
directDependencies.addSignature(lib, src, signature)
fun addDirectDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature, hash: ICHash) =
when (val signatures = directDependencies[lib, src]) {
null -> directDependencies[lib, src] = mutableMapOf(signature to hash)
else -> signatures[signature] = hash
}
}
private class UpdatedDependenciesMetadata(oldMetadata: KotlinSourceFileMetadata) : KotlinSourceFileMetadata() {
private val oldInverseDependencies = oldMetadata.inverseDependencies
private val newExportedSignatures: Set<IdSignature> by lazy { inverseDependencies.flatSignatures() }
var importedInlineFunctionsModified = false
var importedSignaturesModified = false
override val inverseDependencies = oldMetadata.inverseDependencies.toMutable()
override val directDependencies = oldMetadata.directDependencies.toMutable()
override val importedInlineFunctions = oldMetadata.importedInlineFunctions
override fun getExportedSignatures(): Set<IdSignature> = newExportedSignatures
@@ -223,8 +224,8 @@ class CacheUpdater(
if (dependentFile !in dirtyLibFiles) {
val dependentSrcFileMetadata = dependentCache.fetchSourceFileFullMetadata(dependentFile)
dependentSrcFileMetadata.directDependencies[libFile, srcFile]?.let {
loadingFileExports.inverseDependencies[dependentLib, dependentFile] = it
loadingFileExports.allExportedSignatures += it
loadingFileExports.inverseDependencies[dependentLib, dependentFile] = it.keys
loadingFileExports.allExportedSignatures += it.keys
}
}
}
@@ -267,6 +268,7 @@ class CacheUpdater(
val maybeImportedSignatures = reachableSignatures.keys.toMutableSet()
for ((signature, symbol) in allImplementedSymbols) {
if (signature in reachableSignatures) {
signatureHashCalculator.addHashForSignatureIfNotExist(signature, symbol)
idSignatureToFile[signature] = lib to libSrcFile
maybeImportedSignatures.remove(signature)
@@ -295,8 +297,9 @@ class CacheUpdater(
for (importedSignature in internalHeader.maybeImportedSignatures) {
val (dependencyLib, dependencyFile) = idSignatureToFile[importedSignature] ?: continue
internalHeader.addDirectDependency(dependencyLib, dependencyFile, importedSignature)
transitiveHashes[importedSignature]?.let { internalHeader.importedInlineFunctions[importedSignature] = it }
signatureHashCalculator[importedSignature]?.also { signatureHash ->
internalHeader.addDirectDependency(dependencyLib, dependencyFile, importedSignature, signatureHash)
} ?: notFoundIcError("signature $importedSignature hash", dependencyLib, dependencyFile)
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(libFile, srcFile, importedSignature)
@@ -339,7 +342,7 @@ class CacheUpdater(
continue
}
val newMetadata = addNewMetadata(dependencyLibFile, dependencySrcFile, dependencySrcMetadata)
newMetadata.inverseDependencies[libFile, srcFile] = newSignatures
newMetadata.inverseDependencies[libFile, srcFile] = newSignatures.keys
}
}
}
@@ -367,21 +370,25 @@ class CacheUpdater(
private fun KotlinSourceFileMutableMap<UpdatedDependenciesMetadata>.addDependentsWithUpdatedImports(
libFile: KotlinLibraryFile, srcFile: KotlinSourceFile, srcFileMetadata: DirtyFileMetadata
) {
// go through dependent files and check if their inline imports were modified
// go through dependent files and check if their imports were modified
for ((dependentLibFile, dependentSrcFiles) in srcFileMetadata.inverseDependencies) {
val dependentCache = incrementalCaches[dependentLibFile] ?: continue
for ((dependentSrcFile, newSignatures) in dependentSrcFiles) {
val dependentSrcMetadata = dependentCache.fetchSourceFileFullMetadata(dependentSrcFile)
val dependentSignatures = dependentSrcMetadata.directDependencies[libFile, srcFile] ?: emptySet()
val importedInlineModified = dependentSrcMetadata.importedInlineFunctions.any {
transitiveHashes[it.key]?.let { newHash -> newHash != it.value } ?: (it.key in dependentSignatures)
}
if (importedInlineModified) {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.importedInlineFunctionsModified = true
} else if (dependentSignatures != newSignatures) {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.directDependencies[libFile, srcFile] = newSignatures
val dependentSignatures = dependentSrcMetadata.directDependencies[libFile, srcFile] ?: emptyMap()
when {
dependentSrcMetadata is DirtyFileMetadata -> continue
this[dependentLibFile, dependentSrcFile]?.importedSignaturesModified == true -> continue
dependentSignatures.any { signatureHashCalculator[it.key] != it.value } -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.importedSignaturesModified = true
}
dependentSignatures.keys != newSignatures -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata)
newMetadata.directDependencies[libFile, srcFile] = newSignatures.associateWith {
signatureHashCalculator[it] ?: notFoundIcError("signature $it hash", libFile, srcFile)
}
}
}
}
}
@@ -412,15 +419,15 @@ class CacheUpdater(
val cache = getLibIncrementalCache(libFile)
for ((srcFile, srcFileMetadata) in srcFiles) {
val isSignatureUpdated = srcFileMetadata.isExportedSignaturesUpdated()
if (isSignatureUpdated || srcFileMetadata.importedInlineFunctionsModified) {
val isExportedSignatureUpdated = srcFileMetadata.isExportedSignaturesUpdated()
if (isExportedSignatureUpdated || srcFileMetadata.importedSignaturesModified) {
// if exported signatures or imported inline functions were modified - rebuild
filesToRebuild[srcFile] = srcFileMetadata
if (isSignatureUpdated) {
if (isExportedSignatureUpdated) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_EXPORTS)
}
if (srcFileMetadata.importedInlineFunctionsModified) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_INLINE_IMPORTS)
if (srcFileMetadata.importedSignaturesModified) {
fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_IMPORTS)
}
} else {
// if signatures and inline functions are the same - just update cache metadata
@@ -486,7 +493,7 @@ class CacheUpdater(
var lastDirtyFiles: KotlinSourceFileMap<KotlinSourceFileExports> = dirtyFileExports
while (true) {
hashCalculator.updateTransitiveHashes(loadedIr.loadedFragments.values)
signatureHashCalculator.updateInlineFunctionTransitiveHashes(loadedIr.loadedFragments.values)
val dirtyHeaders = rebuildDirtySourceMetadata(loadedIr.linker, loadedIr.loadedFragments, lastDirtyFiles)
@@ -8,6 +8,10 @@ package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CrossModuleReferences
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.library.KotlinLibrary
@@ -85,6 +89,17 @@ internal fun IrElement.irElementHashForIC() = HashCalculatorForIC().also {
)
}.finalize()
internal fun IrSymbol.irSymbolHashForIC() = HashCalculatorForIC().also {
it.update(toString())
// symbol rendering prints very little information about type parameters
// TODO may be it make sense to update rendering?
(owner as? IrTypeParametersContainer)?.let { typeParameters ->
typeParameters.typeParameters.forEach { typeParameter ->
it.update(typeParameter.symbol.toString())
}
}
}.finalize()
internal fun String.stringHashForIC() = HashCalculatorForIC().also { it.update(this) }.finalize()
internal fun KotlinLibrary.fingerprint(fileIndex: Int) = HashCalculatorForIC().apply {
@@ -6,32 +6,33 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
class InlineFunctionTransitiveHashCalculator {
class IdSignatureHashCalculator {
private val flatHashes = mutableMapOf<IrFunction, ICHash>()
private val inlineFunctionCallGraph: MutableMap<IrFunction, Set<IrFunction>> = mutableMapOf()
private val processingFunctions = mutableSetOf<IrFunction>()
private val functionTransitiveHashes = mutableMapOf<IrFunction, ICHash>()
private val idSignatureTransitiveHashes = mutableMapOf<IdSignature, ICHash>()
val transitiveHashes: Map<IdSignature, ICHash>
get() = idSignatureTransitiveHashes
private val allIdSignatureHashes = mutableMapOf<IdSignature, ICHash>()
private inner class FlatHashCalculator : IrElementVisitorVoid {
override fun visitElement(element: IrElement) = element.acceptChildren(this, null)
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
if (declaration.isInline) {
@@ -47,11 +48,11 @@ class InlineFunctionTransitiveHashCalculator {
}
}
// go deeper since local inline special declarations (like a reference adaptor) may appear
declaration.acceptChildren(this, null)
declaration.acceptChildrenVoid(this)
}
}
private inner class CallGraphBuilder : IrElementVisitor<Unit, MutableSet<IrFunction>> {
private inner class InlineFunctionCallGraphBuilder : IrElementVisitor<Unit, MutableSet<IrFunction>> {
var inlineFunctionCallDepth: Int = 0
override fun visitElement(element: IrElement, data: MutableSet<IrFunction>) = element.acceptChildren(this, data)
@@ -103,7 +104,7 @@ class InlineFunctionTransitiveHashCalculator {
functionInlineHash = functionInlineHash.combineWith(getInlineFunctionTransitiveHash(callee))
}
processingFunctions.remove(f)
f.symbol.signature?.let { idSignatureTransitiveHashes[it] = functionInlineHash }
f.symbol.signature?.let { allIdSignatureHashes[it] = functionInlineHash }
functionInlineHash
}
@@ -117,9 +118,17 @@ class InlineFunctionTransitiveHashCalculator {
}
}
fun updateTransitiveHashes(fragments: Collection<IrModuleFragment>) {
fun updateInlineFunctionTransitiveHashes(fragments: Collection<IrModuleFragment>) {
fragments.forEach { it.acceptVoid(FlatHashCalculator()) }
fragments.forEach { it.acceptChildren(CallGraphBuilder(), mutableSetOf()) }
fragments.forEach { it.acceptChildren(InlineFunctionCallGraphBuilder(), mutableSetOf()) }
updateTransitiveHashesByCallGraph()
}
fun addHashForSignatureIfNotExist(signature: IdSignature, symbol: IrSymbol) {
if (signature !in allIdSignatureHashes) {
allIdSignatureHashes[signature] = symbol.irSymbolHashForIC()
}
}
operator fun get(signature: IdSignature) = allIdSignatureHashes[signature]
}
@@ -61,16 +61,12 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
private class KotlinSourceFileMetadataFromDisk(
override val inverseDependencies: KotlinSourceFileMap<Set<IdSignature>>,
override val directDependencies: KotlinSourceFileMap<Set<IdSignature>>,
override val importedInlineFunctions: Map<IdSignature, ICHash>
override val directDependencies: KotlinSourceFileMap<Map<IdSignature, ICHash>>,
) : KotlinSourceFileMetadata()
private object KotlinSourceFileMetadataNotExist : KotlinSourceFileMetadata() {
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
override val directDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
override val importedInlineFunctions = emptyMap<IdSignature, ICHash>()
override val directDependencies = KotlinSourceFileMap<Map<IdSignature, ICHash>>(emptyMap())
}
private val kotlinLibrarySourceFileMetadata = mutableMapOf<KotlinSourceFile, KotlinSourceFileMetadata>()
@@ -165,46 +161,53 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) =
kotlinLibrarySourceFileMetadata.getOrPut(srcFile) {
val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { mutableMapOf() }
fun CodedInputStream.deserializeIdSignatureAndSave(deserializer: IdSignatureDeserializer) = readIdSignature { index ->
val deserializer: IdSignatureDeserializer by lazy {
kotlinLibraryHeader.signatureDeserializers[srcFile] ?: notFoundIcError("signature deserializer", libraryFile, srcFile)
}
fun CodedInputStream.deserializeIdSignatureAndSave() = readIdSignature { index ->
val signature = deserializer.deserializeIdSignature(index)
signatureToIndexMapping[signature] = index
signature
}
val deserializer: IdSignatureDeserializer by lazy {
kotlinLibraryHeader.signatureDeserializers[srcFile] ?: notFoundIcError("signature deserializer", libraryFile, srcFile)
fun <T> CodedInputStream.readDependencies(signaturesReader: () -> T) = buildMapUntil(readInt32()) {
val libraryFile = KotlinLibraryFile.fromProtoStream(this@readDependencies)
val depends = buildMapUntil(readInt32()) {
val dependencySrcFile = KotlinSourceFile.fromProtoStream(this@readDependencies)
put(dependencySrcFile, signaturesReader())
}
put(libraryFile, depends)
}
fun CodedInputStream.readDirectDependencies() = readDependencies {
if (loadSignatures) {
buildMapUntil(readInt32()) {
val signature = deserializeIdSignatureAndSave()
put(signature, ICHash.fromProtoStream(this@readDirectDependencies))
}
} else {
repeat(readInt32()) {
skipIdSignature()
ICHash.fromProtoStream(this@readDirectDependencies)
}
emptyMap()
}
}
fun CodedInputStream.readInverseDependencies() = readDependencies {
if (loadSignatures) {
buildSetUntil(readInt32()) { add(deserializeIdSignatureAndSave()) }
} else {
repeat(readInt32()) { skipIdSignature() }
emptySet()
}
}
srcFile.getCacheFile(METADATA_SUFFIX).useCodedInputIfExists {
fun readDependencies() = buildMapUntil(readInt32()) {
val libraryFile = KotlinLibraryFile.fromProtoStream(this@useCodedInputIfExists)
val depends = buildMapUntil(readInt32()) {
val dependencySrcFile = KotlinSourceFile.fromProtoStream(this@useCodedInputIfExists)
val dependencySignatures = if (loadSignatures) {
buildSetUntil(readInt32()) { add(deserializeIdSignatureAndSave(deserializer)) }
} else {
repeat(readInt32()) { skipIdSignature() }
emptySet()
}
put(dependencySrcFile, dependencySignatures)
}
put(libraryFile, depends)
}
val directDependencies = KotlinSourceFileMap(readDependencies())
val reverseDependencies = KotlinSourceFileMap(readDependencies())
val importedInlineFunctions = if (loadSignatures) {
buildMapUntil(readInt32()) {
val signature = deserializeIdSignatureAndSave(deserializer)
val transitiveHash = ICHash.fromProtoStream(this@useCodedInputIfExists)
put(signature, transitiveHash)
}
} else {
emptyMap()
}
KotlinSourceFileMetadataFromDisk(reverseDependencies, directDependencies, importedInlineFunctions)
val directDependencies = KotlinSourceFileMap(readDirectDependencies())
val reverseDependencies = KotlinSourceFileMap(readInverseDependencies())
KotlinSourceFileMetadataFromDisk(reverseDependencies, directDependencies)
} ?: KotlinSourceFileMetadataNotExist
}
@@ -223,30 +226,36 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
fun CodedOutputStream.serializeIdSignature(signature: IdSignature) =
writeIdSignature(signature) { signatureToIndexMapping[it] ?: signatureToIndexMappingSaved[it] }
headerCacheFile.useCodedOutput {
fun writeDepends(depends: KotlinSourceFileMap<Set<IdSignature>>) {
writeInt32NoTag(depends.size)
for ((dependencyLibFile, dependencySrcFiles) in depends) {
dependencyLibFile.toProtoStream(this)
writeInt32NoTag(dependencySrcFiles.size)
for ((dependencySrcFile, signatures) in dependencySrcFiles) {
dependencySrcFile.toProtoStream(this)
writeInt32NoTag(signatures.size)
for (signature in signatures) {
serializeIdSignature(signature)
}
}
fun <T> CodedOutputStream.writeDependencies(depends: KotlinSourceFileMap<T>, signaturesWriter: (T) -> Unit) {
writeInt32NoTag(depends.size)
for ((dependencyLibFile, dependencySrcFiles) in depends) {
dependencyLibFile.toProtoStream(this)
writeInt32NoTag(dependencySrcFiles.size)
for ((dependencySrcFile, signatures) in dependencySrcFiles) {
dependencySrcFile.toProtoStream(this)
signaturesWriter(signatures)
}
}
}
writeDepends(sourceFileMetadata.directDependencies)
writeDepends(sourceFileMetadata.inverseDependencies)
writeInt32NoTag(sourceFileMetadata.importedInlineFunctions.size)
for ((signature, transitiveHash) in sourceFileMetadata.importedInlineFunctions) {
fun CodedOutputStream.writeDirectDependencies(depends: KotlinSourceFileMap<Map<IdSignature, ICHash>>) = writeDependencies(depends) {
writeInt32NoTag(it.size)
for ((signature, hash) in it) {
serializeIdSignature(signature)
transitiveHash.toProtoStream(this)
hash.toProtoStream(this)
}
}
fun CodedOutputStream.writeInverseDependencies(depends: KotlinSourceFileMap<Set<IdSignature>>) = writeDependencies(depends) {
writeInt32NoTag(it.size)
for (signature in it) {
serializeIdSignature(signature)
}
}
headerCacheFile.useCodedOutput {
writeDirectDependencies(sourceFileMetadata.directDependencies)
writeInverseDependencies(sourceFileMetadata.inverseDependencies)
}
}
}
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.protobuf.CodedOutputStream
import java.io.File
@JvmInline
value class KotlinLibraryFile(val path: String) {
@@ -20,6 +21,9 @@ value class KotlinLibraryFile(val path: String) {
companion object {
fun fromProtoStream(input: CodedInputStream) = KotlinLibraryFile(input.readString())
}
// for debugging purposes only
override fun toString(): String = File(path).name
}
@JvmInline
@@ -31,6 +35,9 @@ value class KotlinSourceFile(val path: String) {
companion object {
fun fromProtoStream(input: CodedInputStream) = KotlinSourceFile(input.readString())
}
// for debugging purposes only
override fun toString(): String = File(path).name
}
open class KotlinSourceFileMap<out T>(files: Map<KotlinLibraryFile, Map<KotlinSourceFile, T>>) :
@@ -94,9 +101,7 @@ abstract class KotlinSourceFileExports {
}
abstract class KotlinSourceFileMetadata : KotlinSourceFileExports() {
abstract val directDependencies: KotlinSourceFileMap<Set<IdSignature>>
abstract val importedInlineFunctions: Map<IdSignature, ICHash>
abstract val directDependencies: KotlinSourceFileMap<Map<IdSignature, ICHash>>
}
fun KotlinSourceFileMetadata.isEmpty() = inverseDependencies.isEmpty() && directDependencies.isEmpty() && importedInlineFunctions.isEmpty()
fun KotlinSourceFileMetadata.isEmpty() = inverseDependencies.isEmpty() && directDependencies.isEmpty()
@@ -3,17 +3,17 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
STEP 2:
dependencies: lib1, lib2
STEP 3..4:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
STEP 5:
dependencies: lib1, lib2
STEP 6:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
STEP 7:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
@@ -10,7 +10,7 @@ STEP 1:
modified ir: m.kt
STEP 2:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
STEP 3:
dependencies: lib1
modifications:
@@ -6,9 +6,9 @@ STEP 1:
modifications:
U : f1.1.kt -> f1.kt
modified ir: f1.kt
updated inline imports: f.kt, f2.kt, f3.kt
updated imports: f.kt, f2.kt, f3.kt
STEP 2:
modifications:
U : f1.0.kt -> f1.kt
modified ir: f1.kt
updated inline imports: f.kt, f2.kt, f3.kt
updated imports: f.kt, f2.kt, f3.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt
STEP 1..2:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
@@ -0,0 +1,2 @@
data class Demo(val x: Int) {
}
@@ -1,3 +0,0 @@
// TODO: After KT-51088, m.kt from main module must be dirty
class Demo(val x: Int) {
}
@@ -1,3 +0,0 @@
// TODO: After KT-51088, m.kt from main module must be dirty
data class Demo(val x: Int) {
}
@@ -0,0 +1,2 @@
class Demo(val x: Int) {
}
@@ -1,3 +1,2 @@
// TODO: After KT-51088, m.kt from main module must be dirty
inline class Demo(val x: Int) {
}
@@ -1,16 +1,19 @@
STEP 0:
modifications:
U : l1.0_simple_class.kt -> l1.kt
U : l1.0_data_class.kt -> l1.kt
added file: l1.kt
STEP 1:
modifications:
U : l1.1_data_class.kt -> l1.kt
U : l1.1_simple_class.kt -> l1.kt
modified ir: l1.kt
updated exports: l1.kt
STEP 2:
modifications:
U : l1.0_simple_class.kt -> l1.kt
modified ir: l1.kt
STEP 3:
modifications:
U : l1.2_inline_class.kt -> l1.kt
modified ir: l1.kt
updated exports: l1.kt
STEP 3:
modifications:
U : l1.1_simple_class.kt -> l1.kt
modified ir: l1.kt
updated exports: l1.kt
@@ -1,8 +1,18 @@
fun box(stepId: Int): String {
val d = Demo(15)
when (stepId) {
0, 2 -> if (Demo(15) == Demo(15)) return "Fail"
1, 3 -> if (Demo(15) != Demo(15)) return "Fail"
0, 2 -> {
if (!testEquals(d, Demo(15))) return "Fail equals"
if (testHashCode(d) != Demo(15).hashCode()) return "Fail hashCode"
if (testToString(d) != "Demo(x=15)") return "Fail toString"
}
1, 3-> {
if (testEquals(d, Demo(15))) return "Fail equals"
if (testHashCode(d) == Demo(15).hashCode()) return "Fail hashCode"
if (testToString(d) != "[object Object]") return "Fail toString"
}
else -> return "Unknown"
}
return "OK"
}
@@ -1,5 +1,6 @@
STEP 0:
dependencies: lib1
added file: m.kt
added file: m.kt, testEquals.kt, testHashCode.kt, testToString.kt
STEP 1..3:
dependencies: lib1
updated imports: m.kt, testEquals.kt, testHashCode.kt, testToString.kt
@@ -0,0 +1,3 @@
fun testEquals(lhs: Demo, rhs: Demo): Boolean {
return lhs == rhs
}
@@ -0,0 +1,3 @@
fun testHashCode(d: Demo): Int {
return d.hashCode()
}
@@ -0,0 +1,3 @@
fun testToString(d: Demo): String {
return d.toString()
}
@@ -5,4 +5,4 @@ STEP 0:
dirty js: lib1, main
STEP 1..3:
libs: lib1, main
dirty js: lib1
dirty js: lib1, main
@@ -5,11 +5,16 @@ STEP 1:
dependencies: lib1
STEP 2:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 3:
dependencies: lib1
modified ir: m.kt
STEP 4..9:
STEP 4..7:
dependencies: lib1
STEP 8:
dependencies: lib1
updated imports: m.kt
STEP 9:
dependencies: lib1
STEP 10..11:
dependencies: lib1
@@ -9,13 +9,13 @@ STEP 1:
STEP 2..3:
libs: lib1, main
dirty js: lib1, main
STEP 4..6:
STEP 4..7:
libs: lib1, main
dirty js: lib1
STEP 7:
STEP 8:
libs: lib1, main
dirty js: lib1
STEP 8..9:
dirty js: lib1, main
STEP 9:
libs: lib1, main
dirty js: lib1
STEP 10..11:
@@ -17,7 +17,7 @@ STEP 3:
modifications:
U : l1.3.kt -> l1.kt
modified ir: l1.kt
updated inline imports: l2.kt
updated imports: l2.kt
STEP 4:
modifications:
U : l2.1.kt -> l2.kt
@@ -35,5 +35,5 @@ STEP 7:
modifications:
U : l2.0.kt -> l2.kt
modified ir: l2.kt
updated inline imports: l1.kt
updated imports: l1.kt
updated exports: l1.kt
@@ -3,9 +3,9 @@ STEP 0:
added file: m.kt
STEP 1..4:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 5:
dependencies: lib1
STEP 6..7:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
@@ -8,12 +8,12 @@ STEP 1:
modifications:
U : A.1.kt -> A.kt
modified ir: A.kt
updated inline imports: B.kt, C.kt
updated imports: B.kt, C.kt
STEP 2:
modifications:
U : B.2.kt -> B.kt
modified ir: B.kt
updated inline imports: C.kt
updated imports: C.kt
STEP 3:
modifications:
U : A.0.kt -> A.kt
@@ -22,9 +22,9 @@ STEP 4:
modifications:
U : B.0.kt -> B.kt
modified ir: B.kt
updated inline imports: C.kt
updated imports: C.kt
STEP 5:
modifications:
U : A.1.kt -> A.kt
modified ir: A.kt
updated inline imports: B.kt, C.kt
updated imports: B.kt, C.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt, classABC.kt
STEP 1..5:
dependencies: lib1
updated inline imports: m.kt, classABC.kt
updated imports: m.kt, classABC.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt, classA.kt
STEP 1..4:
dependencies: lib1
updated inline imports: m.kt, classA.kt
updated imports: m.kt, classA.kt
@@ -3,6 +3,6 @@ STEP 0:
added file: l2.kt
STEP 1:
dependencies: lib1
updated inline imports: l2.kt
updated imports: l2.kt
STEP 2:
dependencies: lib1
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt
STEP 1..2:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt, classA.kt
STEP 1..2:
dependencies: lib1
updated inline imports: m.kt, classA.kt
updated imports: m.kt, classA.kt
@@ -10,13 +10,13 @@ STEP 8:
added file: proxy.kt
STEP 9:
dependencies: lib1
updated inline imports: proxy.kt
updated imports: proxy.kt
STEP 10..14:
dependencies: lib1
modified ir: proxy.kt
STEP 15:
dependencies: lib1
updated inline imports: proxy.kt
updated imports: proxy.kt
STEP 16:
dependencies: lib1
modifications:
@@ -24,10 +24,10 @@ STEP 16:
modified ir: proxy.kt
STEP 17:
dependencies: lib1
updated inline imports: proxy.kt
updated imports: proxy.kt
STEP 18..22:
dependencies: lib1
modified ir: proxy.kt
STEP 23:
dependencies: lib1
updated inline imports: proxy.kt
updated imports: proxy.kt
@@ -3,13 +3,13 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 2..6:
dependencies: lib1
modified ir: m.kt
STEP 7:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 8:
dependencies: lib1, libProxy
modifications:
@@ -17,22 +17,22 @@ STEP 8:
modified ir: m.kt
STEP 9..13:
dependencies: lib1, libProxy
updated inline imports: m.kt
updated imports: m.kt
STEP 14:
dependencies: lib1, libProxy
modified ir: m.kt
STEP 15:
dependencies: lib1, libProxy
updated inline imports: m.kt
updated imports: m.kt
STEP 16:
dependencies: lib1, libProxy
modified ir: m.kt
STEP 17..21:
dependencies: lib1, libProxy
updated inline imports: m.kt
updated imports: m.kt
STEP 22:
dependencies: lib1, libProxy
modified ir: m.kt
STEP 23:
dependencies: lib1, libProxy
updated inline imports: m.kt
updated imports: m.kt
@@ -1,5 +1,8 @@
STEP 0:
dependencies: lib1, lib2
added file: m.kt
STEP 1..4:
STEP 1..3:
dependencies: lib1, lib2
updated imports: m.kt
STEP 4:
dependencies: lib1, lib2
@@ -5,7 +5,7 @@ STEP 0:
dirty js: lib1, lib2, main
STEP 1..3:
libs: lib1, lib2, main
dirty js: lib1
dirty js: lib1, main
STEP 4:
libs: lib1, lib2, main
dirty js: lib2
@@ -3,6 +3,6 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 2:
dependencies: lib1
@@ -3,22 +3,22 @@ STEP 0:
added file: m.kt
STEP 1..2:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 3..4:
dependencies: lib1
modified ir: m.kt
STEP 5:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 6:
dependencies: lib1
modified ir: m.kt
STEP 7:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 8:
dependencies: lib1
modified ir: m.kt
STEP 9:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
@@ -3,10 +3,10 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 2:
dependencies: lib1
modified ir: m.kt
STEP 3:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt
STEP 1..3:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
@@ -3,6 +3,6 @@ STEP 0:
added file: m.kt
STEP 1..3:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
STEP 4..6:
dependencies: lib1
@@ -3,4 +3,4 @@ STEP 0:
added file: l3.kt
STEP 1..2:
dependencies: lib1, lib2
updated inline imports: l3.kt
updated imports: l3.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt
STEP 1..2:
dependencies: lib1, lib2, lib3
updated inline imports: m.kt
updated imports: m.kt
@@ -7,4 +7,4 @@ STEP 1:
U : l22.1.kt -> l22.kt
added file: l22.kt
updated exports: l22.kt
updated inline imports: l2.kt
updated imports: l2.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: l3.kt
STEP 1:
dependencies: lib1, lib2
updated inline imports: l3.kt
updated imports: l3.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1, lib2, lib3
updated inline imports: m.kt
updated imports: m.kt
@@ -1,2 +1 @@
// TODO: After KT-51083, m.kt from main module must be dirty
inline fun foo() = 33
@@ -1,2 +1 @@
// TODO: After KT-51083, m.kt from main module must be dirty
inline fun foo() = 77
@@ -0,0 +1 @@
fun foo() = 100
@@ -10,3 +10,11 @@ STEP 2:
modifications:
U : l1.2.kt -> l1.kt
modified ir: l1.kt
STEP 3:
modifications:
U : l1.0.kt -> l1.kt
modified ir: l1.kt
STEP 4:
modifications:
U : l1.4.kt -> l1.kt
modified ir: l1.kt
@@ -1,8 +1,9 @@
fun box(stepId: Int): String {
when (stepId) {
0 -> if (foo() != 42) return "Fail"
0, 3 -> if (foo() != 42) return "Fail"
1 -> if (foo() != 33) return "Fail"
2 -> if (foo() != 77) return "Fail"
4 -> if (foo() != 100) return "Fail"
else -> return "Unknown"
}
return "OK"
@@ -1,5 +1,8 @@
STEP 0:
dependencies: lib1
added file: m.kt
STEP 1..2:
STEP 1..3:
dependencies: lib1
updated imports: m.kt
STEP 4:
dependencies: lib1
@@ -1,8 +1,8 @@
MODULES: lib1, main
STEP 0:
STEP 0..3:
libs: lib1, main
dirty js: lib1, main
STEP 1..2:
STEP 4:
libs: lib1, main
dirty js: lib1
@@ -1,3 +1,2 @@
// TODO: After KT-51090, m.kt from main module must be dirty
suspend fun fooX() = 11
inline fun fooY() = 22
@@ -1,8 +1,6 @@
STEP 0:
dependencies: lib1
added file: m.kt
STEP 1:
STEP 1..2:
dependencies: lib1
STEP 2:
dependencies: lib1
updated inline imports: m.kt
updated imports: m.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: l2.kt
STEP 1:
dependencies: lib1
updated inline imports: l2.kt
updated imports: l2.kt
@@ -3,4 +3,4 @@ STEP 0:
added file: m.kt
STEP 1:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
@@ -12,7 +12,7 @@ STEP 1:
modified ir: l2a.kt
STEP 2:
dependencies: lib1
updated inline imports: l2b.kt, l2a.kt
updated imports: l2b.kt, l2a.kt
STEP 3:
dependencies: lib1
modifications:
@@ -28,7 +28,7 @@ STEP 5:
modified ir: l2b.kt
STEP 6:
dependencies: lib1
updated inline imports: l2b.kt
updated imports: l2b.kt
STEP 7:
dependencies: lib1
modifications:
@@ -38,4 +38,4 @@ STEP 7:
modified ir: l2b.kt
STEP 8:
dependencies: lib1
updated inline imports: l2a.kt, l2b.kt
updated imports: l2a.kt, l2b.kt
@@ -3,7 +3,7 @@ STEP 0:
added file: m.kt
STEP 1..3:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
STEP 4:
dependencies: lib1, lib2
STEP 5:
@@ -11,4 +11,4 @@ STEP 5:
removed direct depends: m.kt
STEP 6..8:
dependencies: lib1, lib2
updated inline imports: m.kt
updated imports: m.kt
@@ -1,4 +1,3 @@
// TODO: After KT-51099, m.kt from main module must be dirty
interface Producer<T> {
fun produce(): T
}
@@ -1,4 +1,3 @@
// TODO: After KT-51099, m.kt from main module must be dirty
interface Producer<T> {
fun produce(): T
}
@@ -3,3 +3,4 @@ STEP 0:
added file: m.kt
STEP 1..2:
dependencies: lib1
updated imports: m.kt
@@ -1,8 +1,5 @@
MODULES: lib1, main
STEP 0:
STEP 0..2:
libs: lib1, main
dirty js: lib1, main
STEP 1..2:
libs: lib1, main
dirty js: lib1