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");
}
@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");
@@ -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 "<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?
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("<v#$index>")
}
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<IrSymbol, PropertyInstance>()
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<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
@@ -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<IrLocalDelegatedPropertySymbol>()
}
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)
}
@@ -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");
}
@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");
@@ -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");
@@ -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");