@@ -43,54 +43,25 @@ public fun Sequence<InputStream>.loadApiFromJvmClasses(visibilityFilter: (String
4343 val supertypes = listOf (superName) - " java/lang/Object" + interfaces.sorted()
4444
4545 val fieldSignatures = fields
46- .map {
47- val annotationHolders =
48- mVisibility?.members?.get(JvmFieldSignature (it.name, it.desc))?.propertyAnnotation
49- val foundAnnotations = methods.annotationsFor(annotationHolders?.method)
50- it.toFieldBinarySignature(foundAnnotations)
51- }.filter {
52- it.isEffectivelyPublic(classAccess, mVisibility)
53- }.filter {
46+ .map { it.buildFieldSignature(mVisibility, this , classNodeMap) }
47+ .filter { it.field.isEffectivelyPublic(classAccess, mVisibility) }
48+ .filter {
5449 /*
5550 * Filter out 'public static final Companion' field that doesn't constitute public API.
5651 * For that we first check if field corresponds to the 'Companion' class and then
5752 * if companion is effectively public by itself, so the 'Companion' field has the same visibility.
5853 */
59- if (! it.isCompanionField(classNode.kotlinMetadata)) return @filter true
60- val outerKClass = (classNode.kotlinMetadata as KotlinClassMetadata .Class ).toKmClass()
61- val companionName = name + " $" + outerKClass.companionObject
62- // False positive is better than the crash here
63- val companionClass = classNodeMap[companionName] ? : return @filter true
64- val visibility = visibilityMap[companionName] ? : return @filter true
54+ val companionClass = when (it) {
55+ is BasicFieldBinarySignature -> return @filter true
56+ is CompanionFieldBinarySignature -> it.companion
57+ }
58+ val visibility = visibilityMap[companionClass.name] ? : return @filter true
6559 companionClass.isEffectivelyPublic(visibility)
66- }
60+ }.map { it.field }
6761
6862 // NB: this 'map' is O(methods + properties * methods) which may accidentally be quadratic
69- val methodSignatures = methods.map {
70- /* *
71- * For getters/setters, pull the annotations from the property
72- * This is either on the field if any or in a '$annotations' synthetic function.
73- */
74- val annotationHolders =
75- mVisibility?.members?.get(JvmMethodSignature (it.name, it.desc))?.propertyAnnotation
76- val foundAnnotations = ArrayList <AnnotationNode >()
77- if (annotationHolders != null ) {
78- foundAnnotations + = fields.annotationsFor(annotationHolders.field)
79- foundAnnotations + = methods.annotationsFor(annotationHolders.method)
80- }
81-
82- /* *
83- * For synthetic $default methods, pull the annotations from the corresponding method
84- */
85- val alternateDefaultSignature = mVisibility?.name?.let { className ->
86- it.alternateDefaultSignature(className)
87- }
88- foundAnnotations + = methods.annotationsFor(alternateDefaultSignature)
89-
90- it.toMethodBinarySignature(foundAnnotations, alternateDefaultSignature)
91- }.filter {
92- it.isEffectivelyPublic(classAccess, mVisibility)
93- }
63+ val methodSignatures = methods.map { it.buildMethodSignature(mVisibility, this ) }
64+ .filter { it.isEffectivelyPublic(classAccess, mVisibility) }
9465
9566 ClassBinarySignature (
9667 name, superName, outerClassName, supertypes, fieldSignatures + methodSignatures, classAccess,
@@ -102,6 +73,82 @@ public fun Sequence<InputStream>.loadApiFromJvmClasses(visibilityFilter: (String
10273 }
10374}
10475
76+ /* *
77+ * Wraps a [FieldBinarySignature] along with additional information.
78+ */
79+ private sealed class FieldBinarySignatureWrapper (val field : FieldBinarySignature )
80+
81+ /* *
82+ * Wraps a regular field's binary signature.
83+ */
84+ private class BasicFieldBinarySignature (field : FieldBinarySignature ) : FieldBinarySignatureWrapper(field)
85+
86+ /* *
87+ * Wraps a binary signature for a field referencing a companion object.
88+ */
89+ private class CompanionFieldBinarySignature (field : FieldBinarySignature , val companion : ClassNode ) :
90+ FieldBinarySignatureWrapper (field)
91+
92+ private fun FieldNode.buildFieldSignature (
93+ ownerVisibility : ClassVisibility ? ,
94+ ownerClass : ClassNode ,
95+ classes : TreeMap <String , ClassNode >
96+ ): FieldBinarySignatureWrapper {
97+ val annotationHolders =
98+ ownerVisibility?.members?.get(JvmFieldSignature (name, desc))?.propertyAnnotation
99+ val foundAnnotations = mutableListOf<AnnotationNode >()
100+ foundAnnotations.addAll(ownerClass.methods.annotationsFor(annotationHolders?.method))
101+
102+ var companionClass: ClassNode ? = null
103+ if (isCompanionField(ownerClass.kotlinMetadata)) {
104+ /*
105+ * If the field was generated to hold the reference to a companion class's instance,
106+ * then we have to also take all annotations from the companion class an associate it with
107+ * the field. Otherwise, all these annotations will be lost and if the class was marked
108+ * as non-public API using some annotation, then we won't be able to filter out
109+ * the companion field.
110+ */
111+ val companionName = ownerClass.companionName(ownerClass.kotlinMetadata)
112+ companionClass = classes[companionName]
113+ foundAnnotations.addAll(companionClass?.visibleAnnotations.orEmpty())
114+ foundAnnotations.addAll(companionClass?.invisibleAnnotations.orEmpty())
115+ }
116+
117+ val fieldSignature = toFieldBinarySignature(foundAnnotations)
118+ return if (companionClass != null ) {
119+ CompanionFieldBinarySignature (fieldSignature, companionClass)
120+ } else {
121+ BasicFieldBinarySignature (fieldSignature)
122+ }
123+ }
124+
125+ private fun MethodNode.buildMethodSignature (
126+ ownerVisibility : ClassVisibility ? ,
127+ ownerClass : ClassNode
128+ ): MethodBinarySignature {
129+ /* *
130+ * For getters/setters, pull the annotations from the property
131+ * This is either on the field if any or in a '$annotations' synthetic function.
132+ */
133+ val annotationHolders =
134+ ownerVisibility?.members?.get(JvmMethodSignature (name, desc))?.propertyAnnotation
135+ val foundAnnotations = ArrayList <AnnotationNode >()
136+ if (annotationHolders != null ) {
137+ foundAnnotations + = ownerClass.fields.annotationsFor(annotationHolders.field)
138+ foundAnnotations + = ownerClass.methods.annotationsFor(annotationHolders.method)
139+ }
140+
141+ /* *
142+ * For synthetic $default methods, pull the annotations from the corresponding method
143+ */
144+ val alternateDefaultSignature = ownerVisibility?.name?.let { className ->
145+ alternateDefaultSignature(className)
146+ }
147+ foundAnnotations + = ownerClass.methods.annotationsFor(alternateDefaultSignature)
148+
149+ return toMethodBinarySignature(foundAnnotations, alternateDefaultSignature)
150+ }
151+
105152private fun List<MethodNode>.annotationsFor (methodSignature : JvmMethodSignature ? ): List <AnnotationNode > {
106153 if (methodSignature == null ) return emptyList()
107154
0 commit comments