Fix exception from reflection on local delegated properties
The problem was that in JvmSerializerExtension.writeLocalProperties, we only serialized metadata for local properties, but indices generated in MemberCodegen.generatePropertyMetadataArrayFieldIfNeeded were among all delegated properties in the class (not only local). This behaved incorrectly as long as there was a local and a non-local delegated property in the same class. For example, if there were 5 non-local properties and then one local, that local property would get the index 5 and the synthetic signature "<v#5>". But there would only be one Property entry in the metadata, and so reflection would fail here trying to load the 5th element of the list which contains only one element. Now, the index for a local delegated property is computed only as the number of _local_ delegated properties above it in the class, i.e. the first local delegated property gets index 0 (and synthetic signature "<v#0>"), the next one -- index 1, and so on. #KT-23413 Fixed
This commit is contained in:
@@ -197,8 +197,9 @@ class PropertyReferenceCodegen(
|
||||
fun generateCallableReferenceSignature(iv: InstructionAdapter, callable: CallableDescriptor, state: GenerationState) {
|
||||
if (callable is LocalVariableDescriptor) {
|
||||
val asmType = state.bindingContext.get(CodegenBinding.DELEGATED_PROPERTY_METADATA_OWNER, callable)
|
||||
val allDelegatedProperties = state.bindingContext.get(CodegenBinding.DELEGATED_PROPERTIES, asmType)
|
||||
val index = allDelegatedProperties?.indexOf(callable) ?: -1
|
||||
?: throw AssertionError("No delegated property metadata owner for $callable")
|
||||
val localDelegatedProperties = CodegenBinding.getLocalDelegatedProperties(state.bindingContext, asmType)
|
||||
val index = localDelegatedProperties?.indexOf(callable) ?: -1
|
||||
if (index < 0) {
|
||||
throw AssertionError("Local delegated property is not found in $asmType: $callable")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.codegen.binding;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
|
||||
@@ -13,6 +14,7 @@ import org.jetbrains.kotlin.codegen.SamType;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
@@ -72,6 +74,12 @@ public class CodegenBinding {
|
||||
private CodegenBinding() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<LocalVariableDescriptor> getLocalDelegatedProperties(@NotNull BindingContext bindingContext, @NotNull Type owner) {
|
||||
List<VariableDescriptorWithAccessors> properties = bindingContext.get(DELEGATED_PROPERTIES, owner);
|
||||
return properties == null ? null : CollectionsKt.filterIsInstance(properties, LocalVariableDescriptor.class);
|
||||
}
|
||||
|
||||
public static void initTrace(@NotNull GenerationState state) {
|
||||
CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
|
||||
for (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
|
||||
|
||||
+5
-8
@@ -104,16 +104,13 @@ public class JvmSerializerExtension extends SerializerExtension {
|
||||
@NotNull Type classAsmType,
|
||||
@NotNull GeneratedMessageLite.GeneratedExtension<MessageType, List<ProtoBuf.Property>> extension
|
||||
) {
|
||||
List<VariableDescriptorWithAccessors> localVariables = codegenBinding.get(CodegenBinding.DELEGATED_PROPERTIES, classAsmType);
|
||||
List<LocalVariableDescriptor> localVariables = CodegenBinding.getLocalDelegatedProperties(codegenBinding, classAsmType);
|
||||
if (localVariables == null) return;
|
||||
|
||||
for (VariableDescriptorWithAccessors localVariable : localVariables) {
|
||||
if (localVariable instanceof LocalVariableDescriptor) {
|
||||
PropertyDescriptor propertyDescriptor =
|
||||
FakeDescriptorsForReferencesKt.createFreeFakeLocalPropertyDescriptor((LocalVariableDescriptor) localVariable);
|
||||
DescriptorSerializer serializer = DescriptorSerializer.createForLambda(this);
|
||||
proto.addExtension(extension, serializer.propertyProto(propertyDescriptor).build());
|
||||
}
|
||||
for (LocalVariableDescriptor localVariable : localVariables) {
|
||||
PropertyDescriptor propertyDescriptor = FakeDescriptorsForReferencesKt.createFreeFakeLocalPropertyDescriptor(localVariable);
|
||||
DescriptorSerializer serializer = DescriptorSerializer.createForLambda(this);
|
||||
proto.addExtension(extension, serializer.propertyProto(propertyDescriptor).build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
object Delegate {
|
||||
operator fun getValue(thiz: Any?, property: KProperty<*>): String {
|
||||
return property.name + ":" + property.returnType
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
val a by Delegate
|
||||
|
||||
fun test(): String {
|
||||
if (a != "a:kotlin.String") return "Fail a: $a"
|
||||
|
||||
val b by Delegate
|
||||
if (b != "b:kotlin.String") return "Fail b: $b"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
val x by Delegate
|
||||
|
||||
fun box(): String {
|
||||
if (x != "x:kotlin.String") return "Fail x: $x"
|
||||
|
||||
val y by Delegate
|
||||
if (y != "y:kotlin.String") return "Fail y: $y"
|
||||
|
||||
return C().test()
|
||||
}
|
||||
Generated
+5
@@ -18410,6 +18410,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localAndNonLocal.kt")
|
||||
public void testLocalAndNonLocal() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/localAndNonLocal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/localDelegatedProperty.kt");
|
||||
|
||||
+5
@@ -18410,6 +18410,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localAndNonLocal.kt")
|
||||
public void testLocalAndNonLocal() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/localAndNonLocal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/localDelegatedProperty.kt");
|
||||
|
||||
+5
@@ -18410,6 +18410,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/inlineFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localAndNonLocal.kt")
|
||||
public void testLocalAndNonLocal() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/localAndNonLocal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localDelegatedProperty.kt")
|
||||
public void testLocalDelegatedProperty() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/properties/localDelegated/localDelegatedProperty.kt");
|
||||
|
||||
+6
@@ -6838,6 +6838,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineWithJava.kt")
|
||||
public void testInlineWithJava_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineWithJava.kt");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/simple.kt");
|
||||
|
||||
Reference in New Issue
Block a user