1 /*
2 * 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.ClassItem
20 import com.android.tools.metalava.model.Codebase
21 import com.android.tools.metalava.model.FieldItem
22 import com.android.tools.metalava.model.MethodItem
23 import com.android.tools.metalava.model.visitors.ApiVisitor
24
25 /** Visits the API codebase and inserts into the [Api] the classes, methods and fields */
addApisFromCodebasenull26 fun addApisFromCodebase(api: Api, apiLevel: Int, codebase: Codebase) {
27 codebase.accept(object : ApiVisitor(
28 visitConstructorsAsMethods = true,
29 nestInnerClasses = false
30 ) {
31
32 var currentClass: ApiClass? = null
33
34 override fun afterVisitClass(cls: ClassItem) {
35 currentClass = null
36 }
37
38 override fun visitClass(cls: ClassItem) {
39 val newClass = api.addClass(cls.internalName(), apiLevel, cls.deprecated)
40 currentClass = newClass
41
42 if (cls.isClass()) {
43 // The jar files historically contain package private parents instead of
44 // the real API so we need to correct the data we've already read in
45
46 val filteredSuperClass = cls.filteredSuperclass(filterReference)
47 val superClass = cls.superClass()
48 if (filteredSuperClass != superClass && filteredSuperClass != null) {
49 val existing = newClass.superClasses.firstOrNull()?.name
50 val superInternalName = superClass?.internalName()
51 if (existing == superInternalName) {
52 // The bytecode used to point to the old hidden super class. Point
53 // to the real one (that the signature files referenced) instead.
54 val removed = newClass.removeSuperClass(superInternalName)
55 val since = removed?.since ?: apiLevel
56 val entry = newClass.addSuperClass(filteredSuperClass.internalName(), since)
57 // Show that it's also seen here
58 entry.update(apiLevel)
59
60 // Also inherit the interfaces from that API level, unless it was added later
61 val superClassEntry = api.findClass(superInternalName)
62 if (superClassEntry != null) {
63 for (interfaceType in superClass!!.filteredInterfaceTypes(filterReference)) {
64 val interfaceClass = interfaceType.asClass() ?: return
65 var mergedSince = since
66 val interfaceName = interfaceClass.internalName()
67 for (itf in superClassEntry.interfaces) {
68 val currentInterface = itf.name
69 if (interfaceName == currentInterface) {
70 mergedSince = itf.since
71 break
72 }
73 }
74 newClass.addInterface(interfaceClass.internalName(), mergedSince)
75 }
76 }
77 } else {
78 newClass.addSuperClass(filteredSuperClass.internalName(), apiLevel)
79 }
80 } else if (superClass != null) {
81 newClass.addSuperClass(superClass.internalName(), apiLevel)
82 }
83 } else if (cls.isInterface()) {
84 val superClass = cls.superClass()
85 if (superClass != null && !superClass.isJavaLangObject()) {
86 newClass.addInterface(superClass.internalName(), apiLevel)
87 }
88 } else if (cls.isEnum()) {
89 // Implicit super class; match convention from bytecode
90 if (newClass.name != "java/lang/Enum") {
91 newClass.addSuperClass("java/lang/Enum", apiLevel)
92 }
93
94 // Mimic doclava enum methods
95 newClass.addMethod("valueOf(Ljava/lang/String;)L" + newClass.name + ";", apiLevel, false)
96 newClass.addMethod("values()[L" + newClass.name + ";", apiLevel, false)
97 } else if (cls.isAnnotationType()) {
98 // Implicit super class; match convention from bytecode
99 if (newClass.name != "java/lang/annotation/Annotation") {
100 newClass.addSuperClass("java/lang/Object", apiLevel)
101 newClass.addInterface("java/lang/annotation/Annotation", apiLevel)
102 }
103 }
104
105 // Ensure we don't end up with
106 // - <extends name="java/lang/Object"/>
107 // + <extends name="java/lang/Object" removed="29"/>
108 // which can happen because the bytecode always explicitly contains extends java.lang.Object
109 // but in the source code we don't see it, and the lack of presence of this shouldn't be
110 // taken as a sign that we no longer extend object. But only do this if the class didn't
111 // previously extend object and now extends something else.
112 if ((cls.isClass() || cls.isInterface()) &&
113 newClass.superClasses.size == 1 &&
114 newClass.superClasses[0].name == "java/lang/Object") {
115 newClass.addSuperClass("java/lang/Object", apiLevel)
116 }
117
118 for (interfaceType in cls.filteredInterfaceTypes(filterReference)) {
119 val interfaceClass = interfaceType.asClass() ?: return
120 newClass.addInterface(interfaceClass.internalName(), apiLevel)
121 }
122 }
123
124 override fun visitMethod(method: MethodItem) {
125 if (method.isPrivate || method.isPackagePrivate) {
126 return
127 }
128 val name = method.internalName() +
129 // Use "V" instead of the type of the constructor for backwards compatibility
130 // with the older bytecode
131 method.internalDesc(voidConstructorTypes = true)
132 currentClass?.addMethod(name, apiLevel, method.deprecated)
133 }
134
135 override fun visitField(field: FieldItem) {
136 if (field.isPrivate || field.isPackagePrivate) {
137 return
138 }
139
140 currentClass?.addField(field.internalName(), apiLevel, field.deprecated)
141 }
142 })
143 }