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
This commit is contained in:
pyos
2020-10-09 09:37:35 +02:00
committed by Alexander Udalov
parent 2974004de4
commit 1663619606
7 changed files with 103 additions and 20 deletions
@@ -28003,6 +28003,16 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); 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") @TestMetadata("inlineFun.kt")
public void testInlineFun() throws Exception { public void testInlineFun() throws Exception {
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");
@@ -53,10 +53,6 @@ internal val propertyReferencePhase = makeIrFilePhase(
) )
private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
// Reflection metadata for local properties is serialized under the signature "<v#$N>" attached to the containing class.
// This maps properties to values of N.
private val localPropertyIndices = mutableMapOf<IrSymbol, Int>()
// TODO: join IrLocalDelegatedPropertyReference and IrPropertyReference via the class hierarchy? // TODO: join IrLocalDelegatedPropertyReference and IrPropertyReference via the class hierarchy?
private val IrMemberAccessExpression<*>.getter: IrSimpleFunctionSymbol? private val IrMemberAccessExpression<*>.getter: IrSimpleFunctionSymbol?
get() = (this as? IrPropertyReference)?.getter ?: (this as? IrLocalDelegatedPropertyReference)?.getter get() = (this as? IrPropertyReference)?.getter ?: (this as? IrLocalDelegatedPropertyReference)?.getter
@@ -86,12 +82,11 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
context.state.generateOptimizedCallableReferenceSuperClasses context.state.generateOptimizedCallableReferenceSuperClasses
private val IrMemberAccessExpression<*>.propertyContainer: IrDeclarationParent private val IrMemberAccessExpression<*>.propertyContainer: IrDeclarationParent
get() { get() = if (this is IrLocalDelegatedPropertyReference)
var current: IrDeclaration = getter?.owner ?: field?.owner ?: error("Property without getter or field: ${dump()}") currentClassData?.localPropertyOwner(getter)
while (current.parent is IrFunction) ?: throw AssertionError("local property reference before declaration: ${render()}")
current = current.parent as IrFunction // Local delegated property. else
return current.parent 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 // 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. // 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 { private fun IrBuilderWithScope.computeSignatureString(expression: IrMemberAccessExpression<*>): IrExpression {
if (expression is IrLocalDelegatedPropertyReference) { if (expression is IrLocalDelegatedPropertyReference) {
// Local delegated properties are stored as a plain list, and the runtime library extracts the index from this string: // 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("<v#$index>") return irString("<v#$index>")
} }
val getter = expression.getter ?: return irString(expression.field!!.owner.fakeGetterSignature) 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 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<IrSymbol, PropertyInstance>() val kProperties = mutableMapOf<IrSymbol, PropertyInstance>()
val kPropertiesField = context.irFactory.buildField { val kPropertiesField = context.irFactory.buildField {
name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME) name = Name.identifier(JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME)
@@ -188,7 +184,24 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
isStatic = true isStatic = true
visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY visibility = JavaDescriptorVisibilities.PACKAGE_VISIBILITY
} }
var localPropertiesInClass = 0
val localProperties = mutableListOf<IrLocalDelegatedPropertySymbol>()
val localPropertyIndices = mutableMapOf<IrSymbol, Int>()
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 private var currentClassData: ClassData? = null
@@ -197,11 +210,10 @@ private class PropertyReferenceLowering(val context: JvmBackendContext) : IrElem
irFile.transformChildrenVoid() irFile.transformChildrenVoid()
override fun visitClassNew(declaration: IrClass): IrStatement { override fun visitClassNew(declaration: IrClass): IrStatement {
val data = ClassData() val data = ClassData(declaration, currentClassData)
val parentClassData = currentClassData
currentClassData = data currentClassData = data
declaration.transformChildrenVoid() declaration.transformChildrenVoid()
currentClassData = parentClassData currentClassData = data.parent
// Put the new field at the beginning so that static delegated properties with initializers work correctly. // 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. // 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)) irExprBody(irArrayOf(kPropertiesFieldType, initializers))
} }
}) })
}
context.localDelegatedProperties[declaration.attributeOwnerId] = if (data.localProperties.isNotEmpty()) {
data.kProperties.keys.filterIsInstance<IrLocalDelegatedPropertySymbol>() context.localDelegatedProperties[declaration.attributeOwnerId] = data.localProperties
} }
return declaration return declaration
} }
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement { override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement {
localPropertyIndices[declaration.getter.symbol] = currentClassData!!.localPropertiesInClass++ currentClassData!!.rememberLocalProperty(declaration)
return super.visitLocalDelegatedProperty(declaration) return super.visitLocalDelegatedProperty(declaration)
} }
@@ -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
}()
@@ -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" }
@@ -29769,6 +29769,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); 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") @TestMetadata("inlineFun.kt")
public void testInlineFun() throws Exception { public void testInlineFun() throws Exception {
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");
@@ -27390,6 +27390,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public static class LocalDelegated extends AbstractLightAnalysisModeTest { 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 { private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); 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"); 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") @TestMetadata("inlineFun.kt")
public void testInlineFun() throws Exception { public void testInlineFun() throws Exception {
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");
@@ -28003,6 +28003,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/defaultImpls.kt"); 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") @TestMetadata("inlineFun.kt")
public void testInlineFun() throws Exception { public void testInlineFun() throws Exception {
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt"); runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");