From 16636196061081aaf177206328553b55df8ce925 Mon Sep 17 00:00:00 2001 From: pyos Date: Fri, 9 Oct 2020 09:37:35 +0200 Subject: [PATCH] JVM_IR: add local delegated property metadata to non-synthetic classes Otherwise kotlin-reflect won't find it (and it won't even be serialized anyway). #KT-42562 Fixed --- .../ir/FirBlackBoxCodegenTestGenerated.java | 10 ++++ .../jvm/lower/PropertyReferenceLowering.kt | 52 ++++++++++++------- .../properties/localDelegated/inLambda.kt | 11 ++++ .../localDelegated/inLambdaInInline.kt | 20 +++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 10 ++++ .../LightAnalysisModeTestGenerated.java | 10 ++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 10 ++++ 7 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt create mode 100644 compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 18d70957a04..5e0bda16e32 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -28003,6 +28003,16 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); } + @TestMetadata("inLambda.kt") + public void testInLambda() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt"); + } + + @TestMetadata("inLambdaInInline.kt") + public void testInLambdaInInline() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt"); + } + @TestMetadata("inlineFun.kt") public void testInlineFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt index 5aa34a5ecd7..76ece73dc19 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertyReferenceLowering.kt @@ -53,10 +53,6 @@ internal val propertyReferencePhase = makeIrFilePhase( ) private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { - // Reflection metadata for local properties is serialized under the signature "" attached to the containing class. - // This maps properties to values of N. - private val localPropertyIndices = mutableMapOf() - // TODO: join IrLocalDelegatedPropertyReference and IrPropertyReference via the class hierarchy? private val IrMemberAccessExpression<*>.getter: IrSimpleFunctionSymbol? get() = (this as? IrPropertyReference)?.getter ?: (this as? IrLocalDelegatedPropertyReference)?.getter @@ -86,12 +82,11 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem context.state.generateOptimizedCallableReferenceSuperClasses private val IrMemberAccessExpression<*>.propertyContainer: IrDeclarationParent - get() { - var current: IrDeclaration = getter?.owner ?: field?.owner ?: error("Property without getter or field: ${dump()}") - while (current.parent is IrFunction) - current = current.parent as IrFunction // Local delegated property. - return current.parent - } + get() = if (this is IrLocalDelegatedPropertyReference) + currentClassData?.localPropertyOwner(getter) + ?: throw AssertionError("local property reference before declaration: ${render()}") + else + getter?.owner?.parent ?: field?.owner?.parent ?: error("Property without getter or field: ${dump()}") // Plain Java fields do not have a getter, but can be referenced nonetheless. The signature should be the one // that a getter would have, if it existed. @@ -101,7 +96,8 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem private fun IrBuilderWithScope.computeSignatureString(expression: IrMemberAccessExpression<*>): IrExpression { if (expression is IrLocalDelegatedPropertyReference) { // Local delegated properties are stored as a plain list, and the runtime library extracts the index from this string: - val index = localPropertyIndices[expression.getter] ?: throw AssertionError("no index for ${expression.render()}") + val index = currentClassData?.localPropertyIndex(expression.getter) + ?: throw AssertionError("local property reference before declaration: ${expression.render()}") return irString("") } val getter = expression.getter ?: return irString(expression.field!!.owner.fakeGetterSignature) @@ -178,7 +174,7 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem private data class PropertyInstance(val initializer: IrExpression, val index: Int) - private inner class ClassData { + private inner class ClassData(val irClass: IrClass, val parent: ClassData?) { val kProperties = mutableMapOf() val kPropertiesField = context.irFactory.buildField { name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME) @@ -188,7 +184,24 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem isStatic = true visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY } - var localPropertiesInClass = 0 + + val localProperties = mutableListOf() + val localPropertyIndices = mutableMapOf() + val isSynthetic = irClass.metadata !is MetadataSource.File && irClass.metadata !is MetadataSource.Class + + fun localPropertyIndex(getter: IrSymbol): Int? = + localPropertyIndices[getter] ?: parent?.localPropertyIndex(getter) + + fun localPropertyOwner(getter: IrSymbol): IrClass? = + if (getter in localPropertyIndices) irClass else parent?.localPropertyOwner(getter) + + fun rememberLocalProperty(property: IrLocalDelegatedProperty) { + // Prefer to attach metadata to non-synthetic classes, because it won't be serialized otherwise; + // if not possible, though, putting it right here will at least allow non-reflective uses. + val metadataOwner = generateSequence(this) { it.parent }.find { !it.isSynthetic } ?: this + metadataOwner.localPropertyIndices[property.getter.symbol] = metadataOwner.localProperties.size + metadataOwner.localProperties.add(property.symbol) + } } private var currentClassData: ClassData? = null @@ -197,11 +210,10 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem irFile.transformChildrenVoid() override fun visitClassNew(declaration: IrClass): IrStatement { - val data = ClassData() - val parentClassData = currentClassData + val data = ClassData(declaration, currentClassData) currentClassData = data declaration.transformChildrenVoid() - currentClassData = parentClassData + currentClassData = data.parent // Put the new field at the beginning so that static delegated properties with initializers work correctly. // Since we do not cache property references, the new field does not reference anything else. @@ -213,15 +225,15 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem irExprBody(irArrayOf(kPropertiesFieldType, initializers)) } }) - - context.localDelegatedProperties[declaration.attributeOwnerId] = - data.kProperties.keys.filterIsInstance() + } + if (data.localProperties.isNotEmpty()) { + context.localDelegatedProperties[declaration.attributeOwnerId] = data.localProperties } return declaration } override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement { - localPropertyIndices[declaration.getter.symbol] = currentClassData!!.localPropertiesInClass++ + currentClassData!!.rememberLocalProperty(declaration) return super.visitLocalDelegatedProperty(declaration) } diff --git a/compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt b/compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt new file mode 100644 index 00000000000..266206c850f --- /dev/null +++ b/compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt @@ -0,0 +1,11 @@ +// TARGET_BACKEND: JVM +// WITH_REFLECT +import kotlin.reflect.* + +inline operator fun String.getValue(t:Any?, p: KProperty<*>): String = + if (p.returnType.classifier == String::class) this else "fail" + +fun box() = { + val x by "OK" + x +}() diff --git a/compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt b/compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt new file mode 100644 index 00000000000..ea2bb60aa89 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt @@ -0,0 +1,20 @@ +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM +// WITH_REFLECT +// FILE: 1.kt +package test + +import kotlin.reflect.* + +inline operator fun String.getValue(t:Any?, p: KProperty<*>): String = + if (p.returnType.classifier == String::class) this else "fail" + +inline fun foo(crossinline f: () -> String) = { + val x by f() + x +}() + +// FILE: 2.kt +import test.* + +fun box() = foo { "OK" } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 78208c95a36..907a2b2c3da 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -29769,6 +29769,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); } + @TestMetadata("inLambda.kt") + public void testInLambda() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt"); + } + + @TestMetadata("inLambdaInInline.kt") + public void testInLambdaInInline() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt"); + } + @TestMetadata("inlineFun.kt") public void testInlineFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 514a1d249d8..f28f52e0bf7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -27390,6 +27390,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class LocalDelegated extends AbstractLightAnalysisModeTest { + @TestMetadata("inLambdaInInline.kt") + public void ignoreInLambdaInInline() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt"); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } @@ -27403,6 +27408,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); } + @TestMetadata("inLambda.kt") + public void testInLambda() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt"); + } + @TestMetadata("inlineFun.kt") public void testInlineFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index f931ca78313..f1180bede32 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -28003,6 +28003,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); } + @TestMetadata("inLambda.kt") + public void testInLambda() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambda.kt"); + } + + @TestMetadata("inLambdaInInline.kt") + public void testInLambdaInInline() throws Exception { + runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inLambdaInInline.kt"); + } + @TestMetadata("inlineFun.kt") public void testInlineFun() throws Exception { runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");