xref: /aosp_15_r20/external/leakcanary2/shark-hprof/src/main/java/shark/ProguardMappingReader.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)

<lambda>null1 package shark
2 
3 import java.io.FileNotFoundException
4 import java.io.IOException
5 import java.io.InputStream
6 import java.text.ParseException
7 
8 class ProguardMappingReader(
9   private val proguardMappingInputStream: InputStream
10 ) {
11 
12   @Throws(FileNotFoundException::class, IOException::class, ParseException::class)
13   fun readProguardMapping(): ProguardMapping {
14     val proguardMapping = ProguardMapping()
15     proguardMappingInputStream.bufferedReader(Charsets.UTF_8).use { bufferedReader ->
16 
17       var currentClassName: String? = null
18       while (true) {
19         val line = bufferedReader.readLine()?.trim() ?: break
20 
21         if (line.isEmpty() || line.startsWith(HASH_SYMBOL)) {
22           // empty line or comment
23           continue
24         }
25 
26         if (line.endsWith(COLON_SYMBOL)) {
27           currentClassName = parseClassMapping(line, proguardMapping)
28         } else if (currentClassName != null) {
29           val isMethodMapping = line.contains(OPENING_PAREN_SYMBOL)
30           if (!isMethodMapping) {
31             parseClassField(line, currentClassName, proguardMapping)
32           }
33         }
34       }
35     }
36     return proguardMapping
37   }
38 
39   // classes are stored as "clearName -> obfuscatedName:"
40   private fun parseClassMapping(
41     line: String,
42     proguardMapping: ProguardMapping
43   ): String? {
44     val arrowPosition = line.indexOf(ARROW_SYMBOL)
45     if (arrowPosition == -1) {
46       return null
47     }
48 
49     val colonPosition = line.indexOf(COLON_SYMBOL, arrowPosition + ARROW_SYMBOL.length)
50     if (colonPosition == -1) {
51       return null
52     }
53 
54     val clearClassName = line.substring(0, arrowPosition).trim()
55     val obfuscatedClassName =
56       line.substring(arrowPosition + ARROW_SYMBOL.length, colonPosition).trim()
57 
58     proguardMapping.addMapping(obfuscatedClassName, clearClassName)
59 
60     return obfuscatedClassName
61   }
62 
63   // fields are stored as "typeName clearFieldName -> obfuscatedFieldName"
64   private fun parseClassField(
65     line: String,
66     currentClassName: String,
67     proguardMapping: ProguardMapping
68   ) {
69     val spacePosition = line.indexOf(SPACE_SYMBOL)
70     if (spacePosition == -1) {
71       return
72     }
73 
74     val arrowPosition = line.indexOf(ARROW_SYMBOL, spacePosition + SPACE_SYMBOL.length)
75     if (arrowPosition == -1) {
76       return
77     }
78 
79     val clearFieldName = line.substring(spacePosition + SPACE_SYMBOL.length, arrowPosition).trim()
80     val obfuscatedFieldName = line.substring(arrowPosition + ARROW_SYMBOL.length).trim()
81 
82     proguardMapping.addMapping("$currentClassName.$obfuscatedFieldName", clearFieldName)
83   }
84 
85   companion object {
86     private const val HASH_SYMBOL = "#"
87     private const val ARROW_SYMBOL = "->"
88     private const val COLON_SYMBOL = ":"
89     private const val SPACE_SYMBOL = " "
90     private const val OPENING_PAREN_SYMBOL = "("
91   }
92 }