Load annotations of const properties from multifile classes

Rework backing field generation logic in PropertyCodegen to switch the
ClassBuilder instance for a multifile part to that of the corresponding facade
class. This became needed because multifile parts, and their metadata, are
generated _before_ the multifile facade class and otherwise we would never
record that there's a synthetic '$annotations' method for a const val and would
not write that to the protobuf message for the property.

See also bad83200

 #KT-10892 Fixed
This commit is contained in:
Alexander Udalov
2016-03-23 18:55:40 +03:00
parent 6924d883eb
commit a8bebeb48d
14 changed files with 150 additions and 61 deletions
@@ -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<A : Any, C : Any,
val syntheticFunctionSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, synthetic = true)
val fieldSignature = getPropertySignature(proto, container.nameResolver, container.typeTable, field = true)
val isConst = Flags.IS_CONST.get(proto.flags)
val propertyAnnotations = syntheticFunctionSignature?.let { sig ->
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<A : Any, C : Any,
protected abstract fun transformAnnotations(annotations: List<A>): List<T>
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<A> {
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<A : Any, C : Any,
val signature = getCallableSignature(proto, container.nameResolver, container.typeTable, AnnotatedCallableKind.PROPERTY)
?: return null
val kotlinClass = findClassWithAnnotationsAndInitializers(container, getImplClassName(container, property = true), field = true)
?: return null
val implClassName = getImplClassName(container, property = true, isConst = Flags.IS_CONST.get(proto.flags))
val kotlinClass = findClassWithAnnotationsAndInitializers(container, implClassName, field = true) ?: return null
return storage(kotlinClass).propertyConstants[signature]
}
@@ -222,9 +226,20 @@ abstract class AbstractBinaryClassAnnotationAndConstantLoader<A : Any, C : Any,
return null
}
private fun getImplClassName(container: ProtoContainer, property: Boolean): ClassId? {
if (property && container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.INTERFACE) {
return container.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME))
private fun getImplClassName(container: ProtoContainer, property: Boolean, isConst: Boolean?): ClassId? {
if (property) {
checkNotNull(isConst) { "isConst should not be null for property (container=$container)" }
if (container is ProtoContainer.Class && container.kind == ProtoBuf.Class.Kind.INTERFACE) {
return container.classId.createNestedClassId(Name.identifier(JvmAbi.DEFAULT_IMPLS_CLASS_NAME))
}
if (isConst!! && container is ProtoContainer.Package) {
// Const properties in multifile classes are generated into the facade class
val facadeClassName = (container.packagePartSource as? JvmPackagePartSource)?.facadeClassName
if (facadeClassName != null) {
// Converting '/' to '.' is fine here because the facade class has a top level ClassId
return ClassId.topLevel(FqName(facadeClassName.internalName.replace('/', '.')))
}
}
}
return ((container as? ProtoContainer.Package)?.packagePartSource as? JvmPackagePartSource)?.classId
}
@@ -58,7 +58,7 @@ class DeserializedDescriptorResolver(private val errorReporter: ErrorReporter) {
val (nameResolver, packageProto) = parseProto(kotlinClass) {
JvmProtoBufUtil.readPackageDataFrom(data, strings)
}
val source = JvmPackagePartSource(kotlinClass.classId)
val source = JvmPackagePartSource(kotlinClass)
return DeserializedPackageMemberScope(descriptor, packageProto, nameResolver, source, components) {
// All classes are included into Java scope
emptyList()
@@ -21,8 +21,13 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.descriptors.PackagePartSource
class JvmPackagePartSource(val className: JvmClassName) : PackagePartSource {
constructor(classId: ClassId) : this(JvmClassName.byClassId(classId))
class JvmPackagePartSource(val className: JvmClassName, val facadeClassName: JvmClassName?) : PackagePartSource {
constructor(kotlinClass: KotlinJvmBinaryClass) : this(
JvmClassName.byClassId(kotlinClass.classId),
kotlinClass.classHeader.multifileClassName?.let {
if (it.isNotEmpty()) JvmClassName.byInternalName(it) else null
}
)
val simpleName: Name get() = Name.identifier(className.internalName.substringAfterLast('/'))