[JS IR] Fix IC invalidation for fake override inline functions

The patch enables the direct dependency between
 fake override inline function and its implementation from the base class.

 Because klibs do not keep a signature of fake override implementation,
 incremental cache can not use the signature index,
 therefore the signature is serialized to a cache file as is.

^KT-51896 Fixed
This commit is contained in:
Alexander Korepanov
2022-06-14 14:56:10 +02:00
committed by Space
parent c6299ee277
commit 644447db84
43 changed files with 524 additions and 36 deletions
@@ -8,10 +8,10 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrFragmentAndBinaryAst import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrFragmentAndBinaryAst
import org.jetbrains.kotlin.ir.declarations.IrFactory import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.konan.properties.propertyList import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.library.KLIB_PROPERTY_DEPENDS import org.jetbrains.kotlin.library.KLIB_PROPERTY_DEPENDS
@@ -238,6 +238,16 @@ class CacheUpdater(
return exportedSymbols return exportedSymbols
} }
private fun resolveFakeOverrideInlineFunction(symbol: IrSymbol): IdSignature? {
return (symbol.owner as? IrSimpleFunction)?.let { overridable ->
if (overridable.isFakeOverride && overridable.isInline) {
overridable.resolveFakeOverride()?.symbol?.signature
} else {
null
}
}
}
private fun rebuildDirtySourceMetadata( private fun rebuildDirtySourceMetadata(
jsIrLinker: JsIrLinker, jsIrLinker: JsIrLinker,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>, loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
@@ -253,12 +263,15 @@ class CacheUpdater(
val libSrcFile = KotlinSourceFile(fileDeserializer.file) val libSrcFile = KotlinSourceFile(fileDeserializer.file)
val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping() val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
val allImplementedSignatures = fileDeserializer.symbolDeserializer.deserializedSymbols.keys val allImplementedSymbols = fileDeserializer.symbolDeserializer.deserializedSymbols
val maybeImportedSignatures = reachableSignatures.keys.toMutableSet() val maybeImportedSignatures = reachableSignatures.keys.toMutableSet()
for (signature in allImplementedSignatures) { for ((signature, symbol) in allImplementedSymbols) {
if (signature in reachableSignatures) { if (signature in reachableSignatures) {
idSignatureToFile[signature] = lib to libSrcFile idSignatureToFile[signature] = lib to libSrcFile
maybeImportedSignatures.remove(signature) maybeImportedSignatures.remove(signature)
val resolvedSignature = resolveFakeOverrideInlineFunction(symbol) ?: continue
maybeImportedSignatures.add(resolvedSignature)
} }
} }
@@ -38,12 +38,14 @@ internal inline fun File.useCodedOutput(f: CodedOutputStream.() -> Unit) {
} }
} }
internal fun icError(msg: String): Nothing = error("IC internal error: $msg") internal fun icError(what: String, libFile: KotlinLibraryFile? = null, srcFile: KotlinSourceFile? = null): Nothing {
internal fun notFoundIcError(what: String, libFile: KotlinLibraryFile? = null, srcFile: KotlinSourceFile? = null): Nothing {
val filePath = listOfNotNull(libFile?.path, srcFile?.path).joinToString(":") { File(it).name } val filePath = listOfNotNull(libFile?.path, srcFile?.path).joinToString(":") { File(it).name }
val msg = if (filePath.isEmpty()) what else "$what for $filePath" val msg = if (filePath.isEmpty()) what else "$what for $filePath"
icError("can not find $msg") error("IC internal error: $msg")
}
internal fun notFoundIcError(what: String, libFile: KotlinLibraryFile? = null, srcFile: KotlinSourceFile? = null): Nothing {
icError("can not find $what", libFile, srcFile)
} }
internal inline fun <E> buildListUntil(to: Int, builderAction: MutableList<E>.(Int) -> Unit): List<E> { internal inline fun <E> buildListUntil(to: Int, builderAction: MutableList<E>.(Int) -> Unit): List<E> {
@@ -0,0 +1,117 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.protobuf.CodedOutputStream
private enum class IdSignatureProtoType(val id: Int) {
DECLARED_SIGNATURE(0),
COMMON_SIGNATURE(1),
COMPOSITE_SIGNATURE(2),
ACCESSOR_SIGNATURE(3);
}
internal fun CodedOutputStream.writeIdSignature(signature: IdSignature, signatureToIndexMapper: (IdSignature) -> Int?) {
val index = signatureToIndexMapper(signature)
if (index != null) {
writeInt32NoTag(IdSignatureProtoType.DECLARED_SIGNATURE.id)
writeInt32NoTag(index)
return
}
when (signature) {
is IdSignature.CommonSignature -> {
writeInt32NoTag(IdSignatureProtoType.COMMON_SIGNATURE.id)
writeStringNoTag(signature.packageFqName)
writeStringNoTag(signature.declarationFqName)
val id = signature.id
if (id != null) {
writeBoolNoTag(true)
writeFixed64NoTag(id)
} else {
writeBoolNoTag(false)
}
writeInt64NoTag(signature.mask)
}
is IdSignature.CompositeSignature -> {
writeInt32NoTag(IdSignatureProtoType.COMPOSITE_SIGNATURE.id)
writeIdSignature(signature.container, signatureToIndexMapper)
writeIdSignature(signature.inner, signatureToIndexMapper)
}
is IdSignature.AccessorSignature -> {
writeInt32NoTag(IdSignatureProtoType.ACCESSOR_SIGNATURE.id)
writeIdSignature(signature.propertySignature, signatureToIndexMapper)
writeIdSignature(signature.accessorSignature, signatureToIndexMapper)
}
else -> {
icError("can not write $signature signature")
}
}
}
internal fun CodedInputStream.readIdSignature(indexToSignatureMapper: (Int) -> IdSignature): IdSignature {
when (val signatureType = readInt32()) {
IdSignatureProtoType.DECLARED_SIGNATURE.id -> {
return indexToSignatureMapper(readInt32())
}
IdSignatureProtoType.COMMON_SIGNATURE.id -> {
val packageFqName = readString()
val declarationFqName = readString()
val id = if (readBool()) {
readFixed64()
} else {
null
}
val mask = readInt64()
return IdSignature.CommonSignature(packageFqName, declarationFqName, id, mask)
}
IdSignatureProtoType.COMPOSITE_SIGNATURE.id -> {
val containerSignature = readIdSignature(indexToSignatureMapper)
val innerSignature = readIdSignature(indexToSignatureMapper)
return IdSignature.CompositeSignature(containerSignature, innerSignature)
}
IdSignatureProtoType.ACCESSOR_SIGNATURE.id -> {
val propertySignature = readIdSignature(indexToSignatureMapper)
val accessorSignature = readIdSignature(indexToSignatureMapper)
if (accessorSignature !is IdSignature.CommonSignature) {
icError("can not read accessor signature")
}
return IdSignature.AccessorSignature(propertySignature, accessorSignature)
}
else -> {
icError("can not read signature type $signatureType")
}
}
}
internal fun CodedInputStream.skipIdSignature() {
when (val signatureType = readInt32()) {
IdSignatureProtoType.DECLARED_SIGNATURE.id -> {
readInt32()
}
IdSignatureProtoType.COMMON_SIGNATURE.id -> {
readString()
readString()
if (readBool()) {
readFixed64()
}
readInt64()
}
IdSignatureProtoType.COMPOSITE_SIGNATURE.id -> {
skipIdSignature()
skipIdSignature()
}
IdSignatureProtoType.ACCESSOR_SIGNATURE.id -> {
skipIdSignature()
skipIdSignature()
}
else -> {
icError("can not skip signature type $signatureType")
}
}
}
@@ -165,10 +165,10 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) = private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) =
kotlinLibrarySourceFileMetadata.getOrPut(srcFile) { kotlinLibrarySourceFileMetadata.getOrPut(srcFile) {
val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { mutableMapOf() } val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { mutableMapOf() }
fun IdSignatureDeserializer.deserializeIdSignatureAndSave(index: Int): IdSignature { fun CodedInputStream.deserializeIdSignatureAndSave(deserializer: IdSignatureDeserializer) = readIdSignature { index ->
val signature = deserializeIdSignature(index) val signature = deserializer.deserializeIdSignature(index)
signatureToIndexMapping[signature] = index signatureToIndexMapping[signature] = index
return signature signature
} }
val deserializer: IdSignatureDeserializer by lazy { val deserializer: IdSignatureDeserializer by lazy {
@@ -181,9 +181,9 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
val depends = buildMapUntil(readInt32()) { val depends = buildMapUntil(readInt32()) {
val dependencySrcFile = KotlinSourceFile.fromProtoStream(this@useCodedInputIfExists) val dependencySrcFile = KotlinSourceFile.fromProtoStream(this@useCodedInputIfExists)
val dependencySignatures = if (loadSignatures) { val dependencySignatures = if (loadSignatures) {
buildSetUntil(readInt32()) { add(deserializer.deserializeIdSignatureAndSave(readInt32())) } buildSetUntil(readInt32()) { add(deserializeIdSignatureAndSave(deserializer)) }
} else { } else {
repeat(readInt32()) { readInt32() } repeat(readInt32()) { skipIdSignature() }
emptySet() emptySet()
} }
put(dependencySrcFile, dependencySignatures) put(dependencySrcFile, dependencySignatures)
@@ -196,7 +196,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
val importedInlineFunctions = if (loadSignatures) { val importedInlineFunctions = if (loadSignatures) {
buildMapUntil(readInt32()) { buildMapUntil(readInt32()) {
val signature = deserializer.deserializeIdSignatureAndSave(readInt32()) val signature = deserializeIdSignatureAndSave(deserializer)
val transitiveHash = ICHash.fromProtoStream(this@useCodedInputIfExists) val transitiveHash = ICHash.fromProtoStream(this@useCodedInputIfExists)
put(signature, transitiveHash) put(signature, transitiveHash)
} }
@@ -220,10 +220,8 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
} }
val signatureToIndexMappingSaved = signatureToIndexMappingFromMetadata[srcFile] ?: emptyMap() val signatureToIndexMappingSaved = signatureToIndexMappingFromMetadata[srcFile] ?: emptyMap()
fun serializeSignature(signature: IdSignature): Int { fun CodedOutputStream.serializeIdSignature(signature: IdSignature) =
val index = signatureToIndexMapping[signature] ?: signatureToIndexMappingSaved[signature] writeIdSignature(signature) { signatureToIndexMapping[it] ?: signatureToIndexMappingSaved[it] }
return index ?: notFoundIcError("signature $signature", libraryFile, srcFile)
}
headerCacheFile.useCodedOutput { headerCacheFile.useCodedOutput {
fun writeDepends(depends: KotlinSourceFileMap<Set<IdSignature>>) { fun writeDepends(depends: KotlinSourceFileMap<Set<IdSignature>>) {
@@ -235,7 +233,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
dependencySrcFile.toProtoStream(this) dependencySrcFile.toProtoStream(this)
writeInt32NoTag(signatures.size) writeInt32NoTag(signatures.size)
for (signature in signatures) { for (signature in signatures) {
writeInt32NoTag(serializeSignature(signature)) serializeIdSignature(signature)
} }
} }
} }
@@ -246,7 +244,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
writeInt32NoTag(sourceFileMetadata.importedInlineFunctions.size) writeInt32NoTag(sourceFileMetadata.importedInlineFunctions.size)
for ((signature, transitiveHash) in sourceFileMetadata.importedInlineFunctions) { for ((signature, transitiveHash) in sourceFileMetadata.importedInlineFunctions) {
writeInt32NoTag(serializeSignature(signature)) serializeIdSignature(signature)
transitiveHash.toProtoStream(this) transitiveHash.toProtoStream(this)
} }
} }
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.isFakeOverride import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.render 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.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -37,7 +38,13 @@ class InlineFunctionTransitiveHashCalculator {
if (declaration in flatHashes) { if (declaration in flatHashes) {
return return
} }
flatHashes[declaration] = declaration.irElementHashForIC()
flatHashes[declaration] = if (declaration.isFakeOverride) {
declaration.resolveFakeOverride()?.irElementHashForIC()
?: icError("can not resolve fake override for ${declaration.render()}")
} else {
declaration.irElementHashForIC()
}
} }
// go deeper since local inline special declarations (like a reference adaptor) may appear // go deeper since local inline special declarations (like a reference adaptor) may appear
declaration.acceptChildren(this, null) declaration.acceptChildren(this, null)
@@ -61,10 +68,7 @@ class InlineFunctionTransitiveHashCalculator {
override fun visitCall(expression: IrCall, data: MutableSet<IrFunction>) { override fun visitCall(expression: IrCall, data: MutableSet<IrFunction>) {
val callee = expression.symbol.owner val callee = expression.symbol.owner
if (callee.isInline) { if (callee.isInline) {
// TODO: do not ignore fake overrides after KT-51896 data += callee
if (!callee.isFakeOverride) {
data += callee
}
inlineFunctionCallDepth += 1 inlineFunctionCallDepth += 1
} }
expression.acceptChildren(this, data) expression.acceptChildren(this, data)
@@ -80,9 +80,24 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
runTest("js/js.translator/testData/incremental/invalidation/exportsThroughInlineFunction/"); runTest("js/js.translator/testData/incremental/invalidation/exportsThroughInlineFunction/");
} }
@TestMetadata("fakeOverride") @TestMetadata("fakeOverrideInheritance")
public void testFakeOverride() throws Exception { public void testFakeOverrideInheritance() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/fakeOverride/"); runTest("js/js.translator/testData/incremental/invalidation/fakeOverrideInheritance/");
}
@TestMetadata("fakeOverrideInlineExtension")
public void testFakeOverrideInlineExtension() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/fakeOverrideInlineExtension/");
}
@TestMetadata("fakeOverrideInlineFunction")
public void testFakeOverrideInlineFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/fakeOverrideInlineFunction/");
}
@TestMetadata("fakeOverrideInlineProperty")
public void testFakeOverrideInlineProperty() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/fakeOverrideInlineProperty/");
} }
@TestMetadata("fastPath1") @TestMetadata("fastPath1")
@@ -0,0 +1,3 @@
abstract class AbstractClassA {
inline fun funA() = "a-0"
}
@@ -0,0 +1,3 @@
abstract class AbstractClassA {
inline fun funA() = "a-1"
}
@@ -0,0 +1,3 @@
abstract class AbstractClassB : AbstractClassA() {
inline fun funB() = "b-0 ${funA()}"
}
@@ -0,0 +1,3 @@
abstract class AbstractClassB : AbstractClassA() {
inline fun funB() = "b-2"
}
@@ -0,0 +1,3 @@
abstract class AbstractClassC : AbstractClassB() {
inline fun funC() = "c-0 ${funB()}"
}
@@ -0,0 +1,30 @@
STEP 0:
modifications:
U : A.0.kt -> A.kt
U : B.0.kt -> B.kt
U : C.0.kt -> C.kt
added file: A.kt, B.kt, C.kt
STEP 1:
modifications:
U : A.1.kt -> A.kt
modified ir: A.kt
updated inline imports: B.kt, C.kt
STEP 2:
modifications:
U : B.2.kt -> B.kt
modified ir: B.kt
updated inline imports: C.kt
STEP 3:
modifications:
U : A.0.kt -> A.kt
modified ir: A.kt
STEP 4:
modifications:
U : B.0.kt -> B.kt
modified ir: B.kt
updated inline imports: C.kt
STEP 5:
modifications:
U : A.1.kt -> A.kt
modified ir: A.kt
updated inline imports: B.kt, C.kt
@@ -0,0 +1,5 @@
class ClassABC: AbstractClassC() {
inline fun testA() = funA()
inline fun testB() = funB()
inline fun testC() = funC()
}
@@ -0,0 +1,27 @@
fun box(stepId: Int): String {
val a = ClassABC()
when (stepId) {
0, 4 -> {
if (a.testA() != "a-0") return "Fail testA()"
if (a.testB() != "b-0 a-0") return "Fail testB()"
if (a.testC() != "c-0 b-0 a-0") return "Fail testC()"
}
1, 5 -> {
if (a.testA() != "a-1") return "Fail testA()"
if (a.testB() != "b-0 a-1") return "Fail testB()"
if (a.testC() != "c-0 b-0 a-1") return "Fail testC()"
}
2 -> {
if (a.testA() != "a-1") return "Fail testA()"
if (a.testB() != "b-2") return "Fail testB()"
if (a.testC() != "c-0 b-2") return "Fail testC()"
}
3 -> {
if (a.testA() != "a-0") return "Fail testA()"
if (a.testB() != "b-2") return "Fail testB()"
if (a.testC() != "c-0 b-2") return "Fail testC()"
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,6 @@
STEP 0:
dependencies: lib1
added file: m.kt, classABC.kt
STEP 1..5:
dependencies: lib1
updated inline imports: m.kt, classABC.kt
@@ -0,0 +1,8 @@
MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: lib1, main
STEP 1..5:
libs: lib1, main
dirty js: lib1, main
@@ -0,0 +1,14 @@
abstract class AbstractClassA {
inline fun String.fakeOverrideExtension() = "${this} fakeOverrideExtension 0"
inline val String.fakeOverrideGetProperty
get() = "${this} fakeOverrideGetProperty 0"
inline var String.fakeOverrideSetProperty: String
get() = "${savedString} fakeOverrideSetProperty getter 0"
set(str) {
savedString = "${str} fakeOverrideSetProperty setter 0"
}
var savedString = ""
}
@@ -0,0 +1,14 @@
abstract class AbstractClassA {
inline fun String.fakeOverrideExtension() = "${this} fakeOverrideExtension 1"
inline val String.fakeOverrideGetProperty
get() = "${this} fakeOverrideGetProperty 0"
inline var String.fakeOverrideSetProperty: String
get() = "${savedString} fakeOverrideSetProperty getter 0"
set(str) {
savedString = "${str} fakeOverrideSetProperty setter 0"
}
var savedString = ""
}
@@ -0,0 +1,14 @@
abstract class AbstractClassA {
inline fun String.fakeOverrideExtension() = "${this} fakeOverrideExtension 1"
inline val String.fakeOverrideGetProperty
get() = "${this} fakeOverrideGetProperty 2"
inline var String.fakeOverrideSetProperty: String
get() = "${savedString} fakeOverrideSetProperty getter 0"
set(str) {
savedString = "${str} fakeOverrideSetProperty setter 0"
}
var savedString = ""
}
@@ -0,0 +1,14 @@
abstract class AbstractClassA {
inline fun String.fakeOverrideExtension() = "${this} fakeOverrideExtension 1"
inline val String.fakeOverrideGetProperty
get() = "${this} fakeOverrideGetProperty 2"
inline var String.fakeOverrideSetProperty: String
get() = "${savedString} fakeOverrideSetProperty getter 0"
set(str) {
savedString = "${str} fakeOverrideSetProperty setter 3"
}
var savedString = "empty"
}
@@ -0,0 +1,14 @@
abstract class AbstractClassA {
inline fun String.fakeOverrideExtension() = "${this} fakeOverrideExtension 1"
inline val String.fakeOverrideGetProperty
get() = "${this} fakeOverrideGetProperty 2"
inline var String.fakeOverrideSetProperty: String
get() = "${savedString} fakeOverrideSetProperty getter 4"
set(str) {
savedString = "${str} fakeOverrideSetProperty setter 3"
}
var savedString = ""
}
@@ -0,0 +1,20 @@
STEP 0:
modifications:
U : l1.0.kt -> l1.kt
added file: l1.kt
STEP 1:
modifications:
U : l1.1.kt -> l1.kt
modified ir: l1.kt
STEP 2:
modifications:
U : l1.2.kt -> l1.kt
modified ir: l1.kt
STEP 3:
modifications:
U : l1.3.kt -> l1.kt
modified ir: l1.kt
STEP 4:
modifications:
U : l1.4.kt -> l1.kt
modified ir: l1.kt
@@ -0,0 +1,11 @@
class ClassA: AbstractClassA() {
inline fun testExtension() = "testExtension".fakeOverrideExtension()
inline fun testGetProperty() = "testGetProperty".fakeOverrideGetProperty
inline fun testSetPropertySetter(): String {
"test".fakeOverrideSetProperty = "testSetPropertySetter"
return savedString;
}
inline fun testSetPropertyGetter() = "test".fakeOverrideSetProperty
}
@@ -0,0 +1,37 @@
fun box(stepId: Int): String {
val a = ClassA()
when (stepId) {
0 -> {
if (a.testExtension() != "testExtension fakeOverrideExtension 0") return "Fail extension"
if (a.testGetProperty() != "testGetProperty fakeOverrideGetProperty 0") return "Fail getProperty"
if (a.testSetPropertySetter() != "testSetPropertySetter fakeOverrideSetProperty setter 0") return "Fail setPropertySetter"
if (a.testSetPropertyGetter() != "testSetPropertySetter fakeOverrideSetProperty setter 0 fakeOverrideSetProperty getter 0") return a.testSetPropertyGetter()
}
1 -> {
if (a.testExtension() != "testExtension fakeOverrideExtension 1") return "Fail extension"
if (a.testGetProperty() != "testGetProperty fakeOverrideGetProperty 0") return "Fail getProperty"
if (a.testSetPropertySetter() != "testSetPropertySetter fakeOverrideSetProperty setter 0") return "Fail setPropertySetter"
if (a.testSetPropertyGetter() != "testSetPropertySetter fakeOverrideSetProperty setter 0 fakeOverrideSetProperty getter 0") return a.testSetPropertyGetter()
}
2 -> {
if (a.testExtension() != "testExtension fakeOverrideExtension 1") return "Fail extension"
if (a.testGetProperty() != "testGetProperty fakeOverrideGetProperty 2") return "Fail getProperty"
if (a.testSetPropertySetter() != "testSetPropertySetter fakeOverrideSetProperty setter 0") return "Fail setPropertySetter"
if (a.testSetPropertyGetter() != "testSetPropertySetter fakeOverrideSetProperty setter 0 fakeOverrideSetProperty getter 0") return a.testSetPropertyGetter()
}
3 -> {
if (a.testExtension() != "testExtension fakeOverrideExtension 1") return "Fail extension"
if (a.testGetProperty() != "testGetProperty fakeOverrideGetProperty 2") return "Fail getProperty"
if (a.testSetPropertySetter() != "testSetPropertySetter fakeOverrideSetProperty setter 3") return "Fail setPropertySetter"
if (a.testSetPropertyGetter() != "testSetPropertySetter fakeOverrideSetProperty setter 3 fakeOverrideSetProperty getter 0") return a.testSetPropertyGetter()
}
4 -> {
if (a.testExtension() != "testExtension fakeOverrideExtension 1") return "Fail extension"
if (a.testGetProperty() != "testGetProperty fakeOverrideGetProperty 2") return "Fail getProperty"
if (a.testSetPropertySetter() != "testSetPropertySetter fakeOverrideSetProperty setter 3") return "Fail setPropertySetter"
if (a.testSetPropertyGetter() != "testSetPropertySetter fakeOverrideSetProperty setter 3 fakeOverrideSetProperty getter 4") return a.testSetPropertyGetter()
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,6 @@
STEP 0:
dependencies: lib1
added file: m.kt, classA.kt
STEP 1..4:
dependencies: lib1
updated inline imports: m.kt, classA.kt
@@ -0,0 +1,8 @@
MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: lib1, main
STEP 1..4:
libs: lib1, main
dirty js: lib1, main
@@ -1,5 +1,8 @@
STEP 0: STEP 0:
dependencies: lib1 dependencies: lib1
added file: l2.kt added file: l2.kt
STEP 1..2: STEP 1:
dependencies: lib1
updated inline imports: l2.kt
STEP 2:
dependencies: lib1 dependencies: lib1
@@ -23,12 +23,12 @@ fun box(stepId: Int): String {
if (callIt(b::fakeOverrideFunction) != 0) return "Fail 0-6" if (callIt(b::fakeOverrideFunction) != 0) return "Fail 0-6"
} }
1 -> { 1 -> {
if (a.test2() != null) return "Fail 1-1" // TODO: test2() must return "OTHER 2", should be fixed in KT-51896 if (a.test2() != "OTHER 2") return "Fail 1-1"
if (b.test2() != null) return "Fail 1-2" // TODO: test2() must return "OTHER 1", should be fixed in KT-51896 if (b.test2() != "OTHER 1") return "Fail 1-2"
} }
2 -> { 2 -> {
if (a.fakeOverrideFunction() != 0) return "Fail 2-1" // TODO: fakeOverrideFunction() must return 2, should be fixed in KT-51896 if (a.fakeOverrideFunction() != 2) return "Fail 2-1"
if (b.fakeOverrideFunction() != 0) return "Fail 2-2" // TODO: fakeOverrideFunction() must return 2, should be fixed in KT-51896 if (b.fakeOverrideFunction() != 2) return "Fail 2-2"
if (callIt(a::fakeOverrideFunction) != 2) return "Fail 2-3" if (callIt(a::fakeOverrideFunction) != 2) return "Fail 2-3"
if (callIt(b::fakeOverrideFunction) != 2) return "Fail 2-4" if (callIt(b::fakeOverrideFunction) != 2) return "Fail 2-4"
@@ -3,3 +3,4 @@ STEP 0:
added file: m.kt added file: m.kt
STEP 1..2: STEP 1..2:
dependencies: lib1, lib2 dependencies: lib1, lib2
updated inline imports: m.kt
@@ -3,6 +3,9 @@ MODULES: lib1, lib2, main
STEP 0: STEP 0:
libs: lib1, lib2, main libs: lib1, lib2, main
dirty js: lib1, lib2, main dirty js: lib1, lib2, main
STEP 1..2: STEP 1:
libs: lib1, lib2, main libs: lib1, lib2, main
dirty js: lib1 dirty js: lib1, lib2, main
STEP 2:
libs: lib1, lib2, main
dirty js: lib1, main
@@ -0,0 +1,12 @@
abstract class AbstractClassA {
inline val propertyWithGetter: String
get() = "0"
inline var propertyWithSetter: String
get() = savedStr
set(str) {
savedStr = "$str 0"
}
var savedStr = "empty"
}
@@ -0,0 +1,12 @@
abstract class AbstractClassA {
inline val propertyWithGetter: String
get() = "1"
inline var propertyWithSetter: String
get() = savedStr
set(str) {
savedStr = "$str 0"
}
var savedStr = "empty"
}
@@ -0,0 +1,12 @@
abstract class AbstractClassA {
inline val propertyWithGetter: String
get() = "1"
inline var propertyWithSetter: String
get() = savedStr
set(str) {
savedStr = "$str 2"
}
var savedStr = "empty"
}
@@ -0,0 +1,12 @@
STEP 0:
modifications:
U : l1.0.kt -> l1.kt
added file: l1.kt
STEP 1:
modifications:
U : l1.1.kt -> l1.kt
modified ir: l1.kt
STEP 2:
modifications:
U : l1.2.kt -> l1.kt
modified ir: l1.kt
@@ -0,0 +1,8 @@
class ClassA: AbstractClassA() {
inline fun testPropertyWithGetter() = propertyWithGetter
inline fun testPropertyWithSetter(): String {
propertyWithSetter = "test"
return savedStr
}
}
@@ -0,0 +1,19 @@
fun box(stepId: Int): String {
val a = ClassA()
when (stepId) {
0 -> {
if (a.testPropertyWithGetter() != "0") return "Fail testPropertyWithGetter()"
if (a.testPropertyWithSetter() != "test 0") return "Fail testPropertyWithSetter()"
}
1 -> {
if (a.testPropertyWithGetter() != "1") return "Fail testPropertyWithGetter()"
if (a.testPropertyWithSetter() != "test 0") return "Fail testPropertyWithSetter()"
}
2 -> {
if (a.testPropertyWithGetter() != "1") return "Fail testPropertyWithGetter()"
if (a.testPropertyWithSetter() != "test 2") return "Fail testPropertyWithSetter()"
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,6 @@
STEP 0:
dependencies: lib1
added file: m.kt, classA.kt
STEP 1..2:
dependencies: lib1
updated inline imports: m.kt, classA.kt
@@ -0,0 +1,8 @@
MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: lib1, main
STEP 1..2:
libs: lib1, main
dirty js: lib1, main