diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt index efa5cb6ea7e..8b856acd4af 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt @@ -73,7 +73,7 @@ class MultifileClassCodegen( private fun getDeserializedCallables(compiledPackageFragment: PackageFragmentDescriptor) = compiledPackageFragment.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CALLABLES, MemberScope.ALL_NAME_FILTER).filterIsInstance() - private val classBuilder = ClassBuilderOnDemand { + val classBuilder = ClassBuilderOnDemand { val originFile = files.firstOrNull() val actualPackageFragment = packageFragment ?: compiledPackageFragment ?: @@ -163,7 +163,7 @@ class MultifileClassCodegen( var generatePart = false val partClassInfo = state.fileClassesProvider.getFileClassInfo(file) val partType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(partClassInfo.fileClassFqName) - val partContext = state.rootContext.intoMultifileClassPart(packageFragment, facadeClassType, partType, file) + val partContext = state.rootContext.intoMultifileClassPart(this, packageFragment, facadeClassType, partType, file) for (declaration in file.declarations) { when (declaration) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index aeabebcad1f..fc0ac4a8836 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.codegen; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -69,9 +68,6 @@ import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJ import static org.jetbrains.org.objectweb.asm.Opcodes.*; public class PropertyCodegen { - - private static Logger LOG = Logger.getInstance(PropertyCodegen.class); - private final GenerationState state; private final ClassBuilder v; private final FunctionCodegen functionCodegen; @@ -123,7 +119,7 @@ public class PropertyCodegen { assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.DEFAULT_IMPLS : "Generating property with a wrong kind (" + kind + "): " + descriptor; - if (isBackingFieldOwner(descriptor)) { + if (CodegenContextUtil.isImplClassOwner(context)) { assert declaration != null : "Declaration is null for different context: " + context; genBackingFieldAndAnnotations(declaration, descriptor, false); @@ -137,14 +133,10 @@ public class PropertyCodegen { } } - private boolean isBackingFieldOwner(@NotNull PropertyDescriptor descriptor) { - if (descriptor.isConst()) { - return !(context instanceof MultifileClassPartContext); - } - return CodegenContextUtil.isImplClassOwner(context); - } - private void genBackingFieldAndAnnotations(@NotNull KtNamedDeclaration declaration, @NotNull PropertyDescriptor descriptor, boolean isParameter) { + ClassBuilder builder = getCorrectClassBuilder(descriptor); + if (builder == null) return; + boolean hasBackingField = hasBackingField(declaration, descriptor); boolean hasDelegate = declaration instanceof KtProperty && ((KtProperty) declaration).hasDelegate(); @@ -157,8 +149,17 @@ public class PropertyCodegen { Annotations delegateAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD); Annotations propertyAnnotations = annotationSplitter.getAnnotationsForTarget(AnnotationUseSiteTarget.PROPERTY); - generateBackingField(declaration, descriptor, fieldAnnotations, delegateAnnotations); - generateSyntheticMethodIfNeeded(descriptor, propertyAnnotations); + generateBackingField(builder, declaration, descriptor, fieldAnnotations, delegateAnnotations); + generateSyntheticMethodIfNeeded(builder, descriptor, propertyAnnotations); + } + + @Nullable + private ClassBuilder getCorrectClassBuilder(@NotNull PropertyDescriptor descriptor) { + if (descriptor.isConst() && context instanceof MultifileClassPartContext) { + return ((MultifileClassPartContext) context).getMultifileClassCodegen().getClassBuilder(); + } + + return this.v; } /** @@ -244,6 +245,7 @@ public class PropertyCodegen { } private boolean generateBackingField( + @NotNull ClassBuilder builder, @NotNull KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @NotNull Annotations backingFieldAnnotations, @@ -254,10 +256,10 @@ public class PropertyCodegen { } if (p instanceof KtProperty && ((KtProperty) p).hasDelegate()) { - generatePropertyDelegateAccess((KtProperty) p, descriptor, delegateAnnotations); + generatePropertyDelegateAccess(builder, (KtProperty) p, descriptor, delegateAnnotations); } else if (Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor))) { - generateBackingFieldAccess(p, descriptor, backingFieldAnnotations); + generateBackingFieldAccess(builder, p, descriptor, backingFieldAnnotations); } else { return false; @@ -267,16 +269,19 @@ public class PropertyCodegen { // Annotations on properties are stored in bytecode on an empty synthetic method. This way they're still // accessible via reflection, and 'deprecated' and 'private' flags prevent this method from being called accidentally - private void generateSyntheticMethodIfNeeded(@NotNull PropertyDescriptor descriptor, Annotations annotations) { + private void generateSyntheticMethodIfNeeded( + @NotNull ClassBuilder builder, + @NotNull PropertyDescriptor descriptor, + @NotNull Annotations annotations + ) { if (annotations.getAllAnnotations().isEmpty()) return; - ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter(); - String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName()); - String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V"; + Method syntheticMethod = getSyntheticMethodSignature(descriptor); if (!isInterface(context.getContextDescriptor()) || kind == OwnerKind.DEFAULT_IMPLS) { int flags = ACC_DEPRECATED | ACC_FINAL | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC; - MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, name, desc, null, null); + MethodVisitor mv = builder.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, syntheticMethod.getName(), + syntheticMethod.getDescriptor(), null, null); AnnotationCodegen.forMethod(mv, typeMapper) .genAnnotations(new AnnotatedSimple(annotations), Type.VOID_TYPE, AnnotationUseSiteTarget.PROPERTY); mv.visitCode(); @@ -285,15 +290,24 @@ public class PropertyCodegen { } if (kind != OwnerKind.DEFAULT_IMPLS) { - v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, new Method(name, desc)); + v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, syntheticMethod); } } + @NotNull + private Method getSyntheticMethodSignature(@NotNull PropertyDescriptor descriptor) { + ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter(); + String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName()); + String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V"; + return new Method(name, desc); + } + private void generateBackingField( + ClassBuilder builder, KtNamedDeclaration element, PropertyDescriptor propertyDescriptor, boolean isDelegate, - KotlinType jetType, + KotlinType kotlinType, Object defaultValue, Annotations annotations ) { @@ -317,9 +331,7 @@ public class PropertyCodegen { modifiers |= ACC_SYNTHETIC; } - Type type = typeMapper.mapType(jetType); - - ClassBuilder builder = v; + Type type = typeMapper.mapType(kotlinType); FieldOwnerContext backingFieldContext = context; if (AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor) ) { @@ -342,15 +354,22 @@ public class PropertyCodegen { v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, Pair.create(type, name)); - FieldVisitor fv = builder.newField(JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(), - typeMapper.mapFieldSignature(jetType, propertyDescriptor), defaultValue); + FieldVisitor fv = builder.newField( + JvmDeclarationOriginKt.OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(), + typeMapper.mapFieldSignature(kotlinType, propertyDescriptor), defaultValue + ); Annotated fieldAnnotated = new AnnotatedWithFakeAnnotations(propertyDescriptor, annotations); AnnotationCodegen.forField(fv, typeMapper).genAnnotations( fieldAnnotated, type, isDelegate ? AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD : AnnotationUseSiteTarget.FIELD); } - private void generatePropertyDelegateAccess(KtProperty p, PropertyDescriptor propertyDescriptor, Annotations annotations) { + private void generatePropertyDelegateAccess( + @NotNull ClassBuilder builder, + @NotNull KtProperty p, + @NotNull PropertyDescriptor propertyDescriptor, + @NotNull Annotations annotations + ) { KtExpression delegateExpression = p.getDelegateExpression(); KotlinType delegateType = delegateExpression != null ? bindingContext.getType(p.getDelegateExpression()) : null; if (delegateType == null) { @@ -358,10 +377,15 @@ public class PropertyCodegen { delegateType = ErrorUtils.createErrorType("Delegate type"); } - generateBackingField(p, propertyDescriptor, true, delegateType, null, annotations); + generateBackingField(builder, p, propertyDescriptor, true, delegateType, null, annotations); } - private void generateBackingFieldAccess(KtNamedDeclaration p, PropertyDescriptor propertyDescriptor, Annotations annotations) { + private void generateBackingFieldAccess( + @NotNull ClassBuilder builder, + @NotNull KtNamedDeclaration p, + @NotNull PropertyDescriptor propertyDescriptor, + @NotNull Annotations annotations + ) { Object value = null; if (shouldWriteFieldInitializer(propertyDescriptor)) { @@ -371,7 +395,7 @@ public class PropertyCodegen { } } - generateBackingField(p, propertyDescriptor, false, propertyDescriptor.getType(), value, annotations); + generateBackingField(builder, p, propertyDescriptor, false, propertyDescriptor.getType(), value, annotations); } private boolean shouldWriteFieldInitializer(@NotNull PropertyDescriptor descriptor) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java index 3361ed51f4a..754023e68ab 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java @@ -251,12 +251,13 @@ public abstract class CodegenContext { @NotNull public FieldOwnerContext intoMultifileClassPart( + @NotNull MultifileClassCodegen codegen, @NotNull PackageFragmentDescriptor descriptor, @NotNull Type multifileClassType, @NotNull Type filePartType, @NotNull KtFile sourceFile ) { - return new MultifileClassPartContext(descriptor, this, multifileClassType, filePartType, sourceFile); + return new MultifileClassPartContext(codegen, descriptor, this, multifileClassType, filePartType, sourceFile); } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java index 87ac6b61cb6..a6386b5466a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java @@ -18,14 +18,17 @@ package org.jetbrains.kotlin.codegen.context; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.codegen.MultifileClassCodegen; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.org.objectweb.asm.Type; public class MultifileClassPartContext extends MultifileClassContextBase implements DelegatingToPartContext, FacadePartWithSourceFile { private final KtFile sourceFile; + private final MultifileClassCodegen codegen; public MultifileClassPartContext( + @NotNull MultifileClassCodegen codegen, PackageFragmentDescriptor descriptor, CodegenContext parent, Type multifileClassType, @@ -34,6 +37,7 @@ public class MultifileClassPartContext extends MultifileClassContextBase impleme ) { super(descriptor, parent, multifileClassType, filePartType); this.sourceFile = sourceFile; + this.codegen = codegen; } @Nullable @@ -47,4 +51,9 @@ public class MultifileClassPartContext extends MultifileClassContextBase impleme public KtFile getSourceFile() { return sourceFile; } + + @NotNull + public MultifileClassCodegen getMultifileClassCodegen() { + return codegen; + } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt index d513f3ce511..68ab95eab20 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt @@ -115,7 +115,7 @@ class IncrementalPackageFragmentProvider( val scopes = actualPackagePartFiles.mapNotNull { internalName -> incrementalCache.getPackagePartData(internalName)?.let { internalName to it } - }.map { createPackageScope(it.first, it.second) } + }.map { createPackageScope(it.first, it.second, null) } if (scopes.isEmpty()) { MemberScope.Empty @@ -147,7 +147,7 @@ class IncrementalPackageFragmentProvider( else { ChainedMemberScope( "Member scope for incremental compilation: union of multifile class parts data for $multifileClassFqName", - partsData.map { createPackageScope(it.first, it.second) } + partsData.map { createPackageScope(it.first, it.second, multifileClassFqName.asString()) } ) } } @@ -155,11 +155,14 @@ class IncrementalPackageFragmentProvider( override fun getMemberScope(): MemberScope = memberScope() } - fun createPackageScope(internalName: String, part: JvmPackagePartProto): DeserializedPackageMemberScope { + fun createPackageScope(internalName: String, part: JvmPackagePartProto, facadeFqName: String?): DeserializedPackageMemberScope { val packageData = JvmProtoBufUtil.readPackageDataFrom(part.data, part.strings) return DeserializedPackageMemberScope( this, packageData.packageProto, packageData.nameResolver, - JvmPackagePartSource(JvmClassName.byInternalName(internalName)), + JvmPackagePartSource( + JvmClassName.byInternalName(internalName), + facadeFqName?.let(JvmClassName::byFqNameWithoutInnerClasses) + ), deserializationComponents, { listOf() } ) } diff --git a/compiler/testData/codegen/box/multifileClasses/constPropertyReferenceFromMultifileClass.kt b/compiler/testData/codegen/box/multifileClasses/constPropertyReferenceFromMultifileClass.kt index 369756176b4..41fe1657b82 100644 --- a/compiler/testData/codegen/box/multifileClasses/constPropertyReferenceFromMultifileClass.kt +++ b/compiler/testData/codegen/box/multifileClasses/constPropertyReferenceFromMultifileClass.kt @@ -1,4 +1,4 @@ -// WITH_RUNTIME +// WITH_REFLECT // FILE: 1.kt import a.OK @@ -6,12 +6,10 @@ import a.OK fun box(): String { val okRef = ::OK - // TODO: see KT-10892 -// val annotations = okRef.annotations -// val numAnnotations = annotations.size -// if (numAnnotations != 1) { -// return "Failed, annotations: $annotations" -// } + val annotations = okRef.annotations + if (annotations.size != 1) { + return "Failed, annotations: $annotations" + } return okRef.get() } diff --git a/compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.kt b/compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.kt new file mode 100644 index 00000000000..ab2913b0a3d --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.kt @@ -0,0 +1,8 @@ +@file:JvmMultifileClass +@file:JvmName("Test") +package test + +annotation class Anno(val value: String) + +@Anno(constant) +const val constant = "OK" diff --git a/compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.txt b/compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.txt new file mode 100644 index 00000000000..dc5407911f2 --- /dev/null +++ b/compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.txt @@ -0,0 +1,10 @@ +package test + +@test.Anno(value = "OK") public const val constant: kotlin.String = "OK" + public fun (): kotlin.String + +public final annotation class Anno : kotlin.Annotation { + /*primary*/ public constructor Anno(/*0*/ value: kotlin.String) + public final val value: kotlin.String + public final fun (): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index 1b4d1d8a9df..b75730e76da 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -4880,6 +4880,21 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlinWithStdlib"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/annotations") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Annotations extends AbstractLoadJavaTest { + public void testAllFilesPresentInAnnotations() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/loadJava/compiledKotlinWithStdlib/annotations"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ConstValInMultifileClass.kt") + public void testConstValInMultifileClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/annotations/ConstValInMultifileClass.kt"); + doTestCompiledKotlinWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/loadJava/compiledKotlinWithStdlib/mutability") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt index 1fe8ead920c..a0b39a3ea43 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.serialization.Flags import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.deserialization.* import org.jetbrains.kotlin.serialization.jvm.ClassMapperLite @@ -96,12 +97,14 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader - findClassAndLoadMemberAnnotations(container, sig, property = true) + findClassAndLoadMemberAnnotations(container, sig, property = true, isConst = isConst) }.orEmpty() val fieldAnnotations = fieldSignature?.let { sig -> - findClassAndLoadMemberAnnotations(container, sig, property = true, field = true) + findClassAndLoadMemberAnnotations(container, sig, property = true, field = true, isConst = isConst) }.orEmpty() // TODO: check delegate presence in some other way @@ -132,9 +135,10 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader): List private fun findClassAndLoadMemberAnnotations( - container: ProtoContainer, signature: MemberSignature, property: Boolean = false, field: Boolean = false + container: ProtoContainer, signature: MemberSignature, + property: Boolean = false, field: Boolean = false, isConst: Boolean? = null ): List { - val kotlinClass = findClassWithAnnotationsAndInitializers(container, getImplClassName(container, property), field) + val kotlinClass = findClassWithAnnotationsAndInitializers(container, getImplClassName(container, property, isConst), field) ?: return listOf() return storage(kotlinClass).memberAnnotations[signature] ?: listOf() @@ -196,8 +200,8 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader