[KLIB] Make sure that referenced local anonymous class is deserialzied.

In case if such class is used an type argument for field type make sure
that field initializer is being read.

Check type recursively.

- fix KT-41143
- update test
This commit is contained in:
Roman Artemev
2020-11-05 16:29:02 +03:00
parent 0dd329d3d4
commit 8ca9e792e9
2 changed files with 28 additions and 10 deletions
@@ -1170,8 +1170,10 @@ abstract class IrFileDeserializer(
*
* For more information see `anonymousClassLeak.kt` test and issue KT-40216
*/
private fun IrType.checkObjectLeak(isPrivate: Boolean): Boolean {
return isPrivate && this is IrSimpleType && classifier.let { !it.isPublicApi && it !is IrTypeParameterSymbol }
private fun IrType.checkObjectLeak(): Boolean {
return if (this is IrSimpleType) {
classifier.let { !it.isPublicApi && it !is IrTypeParameterSymbol } || arguments.any { it.typeOrNull?.checkObjectLeak() == true }
} else false
}
private fun <T : IrFunction> T.withBodyGuard(block: T.() -> Unit) {
@@ -1180,7 +1182,7 @@ abstract class IrFileDeserializer(
fun checkInlineBody(): Boolean = deserializeInlineFunctions && this is IrSimpleFunction && isInline
try {
deserializeBodies = oldBodiesPolicy || checkInlineBody() || returnType.checkObjectLeak(!symbol.isPublicApi)
deserializeBodies = oldBodiesPolicy || checkInlineBody() || returnType.checkObjectLeak()
block()
} finally {
deserializeBodies = oldBodiesPolicy
@@ -1188,11 +1190,11 @@ abstract class IrFileDeserializer(
}
private fun IrField.withInitializerGuard(isPrivateProperty: Boolean, f: IrField.() -> Unit) {
private fun IrField.withInitializerGuard(f: IrField.() -> Unit) {
val oldBodiesPolicy = deserializeBodies
try {
deserializeBodies = oldBodiesPolicy || type.checkObjectLeak(isPrivateProperty)
deserializeBodies = oldBodiesPolicy || type.checkObjectLeak()
f()
} finally {
deserializeBodies = oldBodiesPolicy
@@ -1319,7 +1321,7 @@ abstract class IrFileDeserializer(
private fun deserializeIrField(proto: ProtoField, isPrivateProperty: Boolean): IrField =
private fun deserializeIrField(proto: ProtoField): IrField =
withDeserializedIrDeclarationBase(proto.base) { symbol, uniqId, startOffset, endOffset, origin, fcode ->
val nameType = BinaryNameAndType.decode(proto.nameType)
val type = deserializeIrType(nameType.typeIndex)
@@ -1337,7 +1339,7 @@ abstract class IrFileDeserializer(
)
}.usingParent {
if (proto.hasInitializer()) {
withInitializerGuard(isPrivateProperty) {
withInitializerGuard {
initializer = irFactory.createExpressionBody(deserializeExpressionBody(proto.initializer))
}
}
@@ -1396,7 +1398,7 @@ abstract class IrFileDeserializer(
}
}
if (proto.hasBackingField()) {
backingField = deserializeIrField(proto.backingField, !symbol.isPublicApi).also {
backingField = deserializeIrField(proto.backingField).also {
// A property symbol and its field symbol share the same descriptor.
// Unfortunately symbol deserialization doesn't know anything about that.
// So we can end up with two wrapped property descriptors for property and its field.
@@ -1444,7 +1446,7 @@ abstract class IrFileDeserializer(
val declaration: IrDeclaration = when (proto.declaratorCase!!) {
IR_ANONYMOUS_INIT -> deserializeIrAnonymousInit(proto.irAnonymousInit)
IR_CONSTRUCTOR -> deserializeIrConstructor(proto.irConstructor)
IR_FIELD -> deserializeIrField(proto.irField, false)
IR_FIELD -> deserializeIrField(proto.irField)
IR_CLASS -> deserializeIrClass(proto.irClass)
IR_FUNCTION -> deserializeIrFunction(proto.irFunction)
IR_PROPERTY -> deserializeIrProperty(proto.irProperty)
+17 -1
View File
@@ -1,3 +1,4 @@
// WITH_RUNTIME
// MODULE: lib
// FILE: lib.kt
@@ -51,6 +52,16 @@ object FunTest {
fun bbb() = qqq().biq().also { it.caz() }
}
object DelegateTest {
var result = ""
val f by lazy {
object { }.also { result += "OK" }
}
fun bbb() = f
}
// MODULE: lib2(lib)
// FILE: lib2.kt
fun test1(): String {
@@ -62,11 +73,16 @@ fun test2(): String {
return FunTest.result
}
fun test3(): String {
DelegateTest.bbb()
return DelegateTest.result
}
// MODULE: main(lib2)
// FILE: main.kt
fun box(): String {
if (test1() != "!abcd") return "FAIL 1: ${test1()}"
if (test2() != "zyxw") return "FAIL 2: ${test2()}"
return "OK"
return test3()
}