1 /* <lambda>null2 * Copyright (C) 2023 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.hoststubgen.asm 17 18 import com.android.hoststubgen.ClassParseException 19 import com.android.hoststubgen.InvalidJarFileException 20 import com.android.hoststubgen.log 21 import org.objectweb.asm.ClassReader 22 import org.objectweb.asm.tree.AnnotationNode 23 import org.objectweb.asm.tree.ClassNode 24 import org.objectweb.asm.tree.FieldNode 25 import org.objectweb.asm.tree.MethodNode 26 import org.objectweb.asm.tree.TypeAnnotationNode 27 import java.io.BufferedInputStream 28 import java.io.PrintWriter 29 import java.util.Arrays 30 import java.util.zip.ZipFile 31 32 /** 33 * Stores all classes loaded from a jar file, in a form of [ClassNode] 34 */ 35 class ClassNodes { 36 val mAllClasses: MutableMap<String, ClassNode> = HashMap() 37 38 /** 39 * Total number of classes registered. 40 */ 41 val size: Int 42 get() = mAllClasses.size 43 44 /** Add a [ClassNode] */ 45 fun addClass(cn: ClassNode): Boolean { 46 if (mAllClasses.containsKey(cn.name)) { 47 return false 48 } 49 mAllClasses[cn.name.toJvmClassName()] = cn 50 return true 51 } 52 53 /** Get a class's [ClassNodes] (which may not exist) */ 54 fun findClass(name: String): ClassNode? { 55 return mAllClasses[name.toJvmClassName()] 56 } 57 58 /** Get a class's [ClassNodes] (which must exists) */ 59 fun getClass(name: String): ClassNode { 60 return findClass(name) ?: throw ClassParseException("Class $name not found") 61 } 62 63 /** @return whether a class exists or not */ 64 fun hasClass(name: String): Boolean { 65 return mAllClasses.containsKey(name.toJvmClassName()) 66 } 67 68 /** Find a field, which may not exist. */ 69 fun findField( 70 className: String, 71 fieldName: String, 72 ): FieldNode? { 73 return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn -> 74 return fn 75 } 76 } 77 78 /** Find a method, which may not exist. */ 79 fun findMethod( 80 className: String, 81 methodName: String, 82 descriptor: String, 83 ): MethodNode? { 84 return findClass(className)?.methods 85 ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn -> 86 return mn 87 } 88 } 89 90 /** @return true if a class has a class initializer. */ 91 fun hasClassInitializer(className: String): Boolean { 92 return findMethod(className, CLASS_INITIALIZER_NAME, CLASS_INITIALIZER_DESC) != null 93 } 94 95 /** Run the lambda on each class in alphabetical order. */ 96 fun forEach(consumer: (classNode: ClassNode) -> Unit) { 97 val keys = mAllClasses.keys.toTypedArray() 98 Arrays.sort(keys) 99 100 for (name in keys) { 101 consumer(mAllClasses[name]!!) 102 } 103 } 104 105 /** 106 * Dump all classes. 107 */ 108 fun dump(pw: PrintWriter) { 109 forEach { classNode -> dumpClass(pw, classNode) } 110 } 111 112 private fun dumpClass(pw: PrintWriter, cn: ClassNode) { 113 pw.printf("Class: %s [access: %x]\n", cn.name, cn.access) 114 dumpAnnotations( 115 pw, " ", 116 cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations, 117 cn.visibleAnnotations, cn.invisibleAnnotations, 118 ) 119 120 for (f in cn.fields ?: emptyList()) { 121 pw.printf( 122 " Field: %s [sig: %s] [desc: %s] [access: %x]\n", 123 f.name, f.signature, f.desc, f.access 124 ) 125 dumpAnnotations( 126 pw, " ", 127 f.visibleTypeAnnotations, f.invisibleTypeAnnotations, 128 f.visibleAnnotations, f.invisibleAnnotations, 129 ) 130 } 131 for (m in cn.methods ?: emptyList()) { 132 pw.printf( 133 " Method: %s [sig: %s] [desc: %s] [access: %x]\n", 134 m.name, m.signature, m.desc, m.access 135 ) 136 dumpAnnotations( 137 pw, " ", 138 m.visibleTypeAnnotations, m.invisibleTypeAnnotations, 139 m.visibleAnnotations, m.invisibleAnnotations, 140 ) 141 } 142 } 143 144 private fun dumpAnnotations( 145 pw: PrintWriter, 146 prefix: String, 147 visibleTypeAnnotations: List<TypeAnnotationNode>?, 148 invisibleTypeAnnotations: List<TypeAnnotationNode>?, 149 visibleAnnotations: List<AnnotationNode>?, 150 invisibleAnnotations: List<AnnotationNode>?, 151 ) { 152 for (an in visibleTypeAnnotations ?: emptyList()) { 153 pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc) 154 } 155 for (an in invisibleTypeAnnotations ?: emptyList()) { 156 pw.printf("%sTypeAnnotation(inv): %s\n", prefix, an.desc) 157 } 158 for (an in visibleAnnotations ?: emptyList()) { 159 pw.printf("%sAnnotation(vis): %s\n", prefix, an.desc) 160 if (an.values == null) { 161 continue 162 } 163 var i = 0 164 while (i < an.values.size - 1) { 165 pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1]) 166 i += 2 167 } 168 } 169 for (an in invisibleAnnotations ?: emptyList()) { 170 pw.printf("%sAnnotation(inv): %s\n", prefix, an.desc) 171 if (an.values == null) { 172 continue 173 } 174 var i = 0 175 while (i < an.values.size - 1) { 176 pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1]) 177 i += 2 178 } 179 } 180 } 181 182 companion object { 183 /** 184 * Load all the classes, without code. 185 */ 186 fun loadClassStructures(inJar: String): ClassNodes { 187 log.i("Reading class structure from $inJar ...") 188 val start = System.currentTimeMillis() 189 190 val allClasses = ClassNodes() 191 192 log.withIndent { 193 ZipFile(inJar).use { inZip -> 194 val inEntries = inZip.entries() 195 196 while (inEntries.hasMoreElements()) { 197 val entry = inEntries.nextElement() 198 199 BufferedInputStream(inZip.getInputStream(entry)).use { bis -> 200 if (entry.name.endsWith(".class")) { 201 val cr = ClassReader(bis) 202 val cn = ClassNode() 203 cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG 204 or ClassReader.SKIP_FRAMES) 205 if (!allClasses.addClass(cn)) { 206 log.w("Duplicate class found: ${cn.name}") 207 } 208 } else if (entry.name.endsWith(".dex")) { 209 // Seems like it's an ART jar file. We can't process it. 210 // It's a fatal error. 211 throw InvalidJarFileException( 212 "$inJar is not a desktop jar file. It contains a *.dex file.") 213 } else { 214 // Unknown file type. Skip. 215 while (bis.available() > 0) { 216 bis.skip((1024 * 1024).toLong()) 217 } 218 } 219 } 220 } 221 } 222 } 223 if (allClasses.size == 0) { 224 log.w("$inJar contains no *.class files.") 225 } 226 227 val end = System.currentTimeMillis() 228 log.i("Done reading class structure in %.1f second(s).", (end - start) / 1000.0) 229 return allClasses 230 } 231 } 232 }