1 /*
<lambda>null2  * Copyright (C) 2022 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 package com.android.tools.metalava.apilevels
17 
18 import com.android.SdkConstants
19 import java.io.File
20 import java.io.FileInputStream
21 import java.util.zip.ZipInputStream
22 import org.objectweb.asm.ClassReader
23 import org.objectweb.asm.Opcodes
24 import org.objectweb.asm.tree.ClassNode
25 import org.objectweb.asm.tree.FieldNode
26 import org.objectweb.asm.tree.MethodNode
27 
28 fun Api.readAndroidJar(apiLevel: Int, jar: File) {
29     update(apiLevel)
30     readJar(apiLevel, jar)
31 }
32 
readExtensionJarnull33 fun Api.readExtensionJar(extensionVersion: Int, module: String, jar: File, nextApiLevel: Int) {
34     readJar(nextApiLevel, jar, extensionVersion, module)
35 }
36 
readJarnull37 fun Api.readJar(apiLevel: Int, jar: File, extensionVersion: Int? = null, module: String? = null) {
38     val fis = FileInputStream(jar)
39     ZipInputStream(fis).use { zis ->
40         var entry = zis.nextEntry
41         while (entry != null) {
42             if (!entry.name.endsWith(SdkConstants.DOT_CLASS)) {
43                 entry = zis.nextEntry
44                 continue
45             }
46             val bytes = zis.readBytes()
47             val reader = ClassReader(bytes)
48             val classNode = ClassNode(Opcodes.ASM5)
49             reader.accept(classNode, 0)
50 
51             val classDeprecated = isDeprecated(classNode.access)
52             val theClass =
53                 addClass(
54                     classNode.name,
55                     apiLevel,
56                     classDeprecated,
57                 )
58             extensionVersion?.let { theClass.updateExtension(extensionVersion) }
59             module?.let { theClass.updateMainlineModule(module) }
60 
61             theClass.updateHidden(apiLevel, (classNode.access and Opcodes.ACC_PUBLIC) == 0)
62 
63             // super class
64             if (classNode.superName != null) {
65                 theClass.addSuperClass(classNode.superName, apiLevel)
66             }
67 
68             // interfaces
69             for (interfaceName in classNode.interfaces) {
70                 theClass.addInterface(interfaceName, apiLevel)
71             }
72 
73             // fields
74             for (field in classNode.fields) {
75                 val fieldNode = field as FieldNode
76                 if ((fieldNode.access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) == 0) {
77                     continue
78                 }
79                 if (!fieldNode.name.startsWith("this\$") && fieldNode.name != "\$VALUES") {
80                     val apiField =
81                         theClass.addField(
82                             fieldNode.name,
83                             apiLevel,
84                             classDeprecated || isDeprecated(fieldNode.access),
85                         )
86                     extensionVersion?.let { apiField.updateExtension(extensionVersion) }
87                 }
88             }
89 
90             // methods
91             for (method in classNode.methods) {
92                 val methodNode = method as MethodNode
93                 if ((methodNode.access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) == 0) {
94                     continue
95                 }
96                 if (methodNode.name != "<clinit>") {
97                     val apiMethod =
98                         theClass.addMethod(
99                             methodNode.name + methodNode.desc,
100                             apiLevel,
101                             classDeprecated || isDeprecated(methodNode.access),
102                         )
103                     extensionVersion?.let { apiMethod.updateExtension(extensionVersion) }
104                 }
105             }
106 
107             entry = zis.nextEntry
108         }
109     }
110 }
111 
isDeprecatednull112 private fun isDeprecated(access: Int) = (access and Opcodes.ACC_DEPRECATED) != 0
113