1 /*
<lambda>null2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.tools.metalava.apilevels
18
19 import com.android.tools.metalava.model.CallableItem
20 import com.android.tools.metalava.model.ClassItem
21 import com.android.tools.metalava.model.ClassKind
22 import com.android.tools.metalava.model.CodebaseFragment
23 import com.android.tools.metalava.model.ConstructorItem
24 import com.android.tools.metalava.model.DelegatedVisitor
25 import com.android.tools.metalava.model.FieldItem
26 import com.android.tools.metalava.model.Item
27 import com.android.tools.metalava.model.MethodItem
28
29 /**
30 * Visits the API codebase and inserts into the [Api] the classes, methods and fields.
31 *
32 * The [Item]s to be visited is determined by the [codebaseFragment].
33 */
34 fun addApisFromCodebase(
35 api: Api,
36 updater: ApiHistoryUpdater,
37 codebaseFragment: CodebaseFragment,
38 ) {
39 val useInternalNames = api.useInternalNames
40
41 // Keep track of the versions added to this api, if necessary.
42 updater.update(api)
43
44 val delegatedVisitor =
45 object : DelegatedVisitor {
46
47 var currentClass: ApiClass? = null
48
49 override fun afterVisitClass(cls: ClassItem) {
50 currentClass = null
51 }
52
53 override fun visitClass(cls: ClassItem) {
54 val newClass = api.updateClass(cls.nameInApi(), updater, cls.effectivelyDeprecated)
55 currentClass = newClass
56
57 when (cls.classKind) {
58 ClassKind.CLASS -> {
59 val superClass = cls.superClass()
60 if (superClass != null) {
61 newClass.updateSuperClass(superClass.nameInApi(), updater)
62 }
63 }
64 ClassKind.INTERFACE -> {
65 // Implicit super class; match convention from bytecode
66 newClass.updateSuperClass(objectClass, updater)
67 }
68 ClassKind.ENUM -> {
69 // Implicit super class; match convention from bytecode
70 if (newClass.name != enumClass) {
71 newClass.updateSuperClass(enumClass, updater)
72 }
73
74 // Mimic doclava enum methods
75 enumMethodNames(newClass.name).forEach { name ->
76 newClass.updateMethod(name, updater, false)
77 }
78 }
79 ClassKind.ANNOTATION_TYPE -> {
80 // Implicit super class; match convention from bytecode
81 if (newClass.name != annotationClass) {
82 newClass.updateSuperClass(objectClass, updater)
83 newClass.updateInterface(annotationClass, updater)
84 }
85 }
86 }
87
88 for (interfaceType in cls.interfaceTypes()) {
89 val interfaceClass = interfaceType.asClass() ?: return
90 newClass.updateInterface(interfaceClass.nameInApi(), updater)
91 }
92 }
93
94 private fun visitCallable(callable: CallableItem) {
95 if (callable.isPrivate || callable.isPackagePrivate) {
96 return
97 }
98 currentClass?.updateMethod(
99 callable.nameInApi(),
100 updater,
101 callable.effectivelyDeprecated
102 )
103 }
104
105 override fun visitConstructor(constructor: ConstructorItem) {
106 visitCallable(constructor)
107 }
108
109 override fun visitMethod(method: MethodItem) {
110 visitCallable(method)
111 }
112
113 override fun visitField(field: FieldItem) {
114 if (field.isPrivate || field.isPackagePrivate) {
115 return
116 }
117 currentClass?.updateField(field.nameInApi(), updater, field.effectivelyDeprecated)
118 }
119
120 /** The name of the field in this [Api], based on [Api.useInternalNames] */
121 fun FieldItem.nameInApi(): String {
122 return if (useInternalNames) {
123 internalName()
124 } else {
125 name()
126 }
127 }
128
129 /** The name of the method in this [Api], based on [Api.useInternalNames] */
130 fun CallableItem.nameInApi(): String {
131 return if (useInternalNames) {
132 internalName() +
133 // Use "V" instead of the type of the constructor for backwards
134 // compatibility
135 // with the older bytecode
136 internalDesc(voidConstructorTypes = true)
137 } else {
138 val paramString = parameters().joinToString(",") { it.type().toTypeString() }
139 name() + typeParameterList + "(" + paramString + ")"
140 }
141 }
142
143 /** The name of the class in this [Api], based on [Api.useInternalNames] */
144 fun ClassItem.nameInApi(): String {
145 return if (useInternalNames) {
146 internalName()
147 } else {
148 qualifiedName()
149 }
150 }
151
152 // The names of some common classes, based on [useInternalNames]
153 val objectClass = nameForClass("java", "lang", "Object")
154 val annotationClass = nameForClass("java", "lang", "annotation", "Annotation")
155 val enumClass = nameForClass("java", "lang", "Enum")
156
157 /** Generates a class name from the package and class names in [nameParts] */
158 fun nameForClass(vararg nameParts: String): String {
159 val separator = if (useInternalNames) "/" else "."
160 return nameParts.joinToString(separator)
161 }
162
163 /** The names of the doclava enum methods, based on [Api.useInternalNames] */
164 fun enumMethodNames(className: String): List<String> {
165 return if (useInternalNames) {
166 listOf("valueOf(Ljava/lang/String;)L$className;", "values()[L$className;")
167 } else {
168 listOf("valueOf(java.lang.String)", "values()")
169 }
170 }
171 }
172
173 codebaseFragment.accept(delegatedVisitor)
174 }
175
176 /**
177 * Like [CallableItem.internalName] but is the desc-portion of the internal signature, e.g. for the
178 * method "void create(int x, int y)" the internal name of the constructor is "create" and the desc
179 * is "(II)V"
180 */
CallableItemnull181 fun CallableItem.internalDesc(voidConstructorTypes: Boolean = false): String {
182 val sb = StringBuilder()
183 sb.append("(")
184
185 // Inner, i.e. non-static nested, classes get an implicit constructor parameter for the
186 // outer type
187 if (
188 isConstructor() &&
189 containingClass().containingClass() != null &&
190 !containingClass().modifiers.isStatic()
191 ) {
192 sb.append(containingClass().containingClass()?.type()?.internalName() ?: "")
193 }
194
195 for (parameter in parameters()) {
196 sb.append(parameter.type().internalName())
197 }
198
199 sb.append(")")
200 sb.append(if (voidConstructorTypes && isConstructor()) "V" else returnType().internalName())
201 return sb.toString()
202 }
203