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:
Generated
+10
@@ -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");
|
||||||
|
|||||||
+32
-20
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
@@ -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
|
||||||
|
}()
|
||||||
+20
@@ -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" }
|
||||||
+10
@@ -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");
|
||||||
|
|||||||
+10
@@ -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");
|
||||||
|
|||||||
+10
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user