1 /*
2  * 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.filters
17 
18 import com.android.hoststubgen.addNonNullElement
19 import com.android.hoststubgen.asm.ClassNodes
20 import com.android.hoststubgen.asm.toHumanReadableClassName
21 import com.android.hoststubgen.asm.toHumanReadableMethodName
22 import com.android.hoststubgen.log
23 
24 // TODO: Validate all input names.
25 
26 class InMemoryOutputFilter(
27     private val classes: ClassNodes,
28     fallback: OutputFilter,
29 ) : DelegatingFilter(fallback) {
30     private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
31     private val mRenames: MutableMap<String, String> = mutableMapOf()
32     private val mNativeSubstitutionClasses: MutableMap<String, String> = mutableMapOf()
33     private val mClassLoadHooks: MutableMap<String, String> = mutableMapOf()
34 
getClassKeynull35     private fun getClassKey(className: String): String {
36         return className.toHumanReadableClassName()
37     }
38 
getFieldKeynull39     private fun getFieldKey(className: String, fieldName: String): String {
40         return getClassKey(className) + "." + fieldName
41     }
42 
getMethodKeynull43     private fun getMethodKey(className: String, methodName: String, signature: String): String {
44         return getClassKey(className) + "." + methodName + ";" + signature
45     }
46 
getPolicyForClassnull47     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
48         return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
49     }
50 
checkClassnull51     private fun checkClass(className: String) {
52         if (classes.findClass(className) == null) {
53             log.w("Unknown class $className")
54         }
55     }
56 
checkFieldnull57     private fun checkField(className: String, fieldName: String) {
58         if (classes.findField(className, fieldName) == null) {
59             log.w("Unknown field $className.$fieldName")
60         }
61     }
62 
checkMethodnull63     private fun checkMethod(
64         className: String,
65         methodName: String,
66         descriptor: String
67     ) {
68         if (classes.findMethod(className, methodName, descriptor) == null) {
69             log.w("Unknown method $className.$methodName$descriptor")
70         }
71     }
72 
setPolicyForClassnull73     fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) {
74         checkClass(className)
75         mPolicies[getClassKey(className)] = policy
76     }
77 
getPolicyForFieldnull78     override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
79         return mPolicies[getFieldKey(className, fieldName)]
80                 ?: super.getPolicyForField(className, fieldName)
81     }
82 
setPolicyForFieldnull83     fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
84         checkField(className, fieldName)
85         mPolicies[getFieldKey(className, fieldName)] = policy
86     }
87 
getPolicyForMethodnull88     override fun getPolicyForMethod(
89             className: String,
90             methodName: String,
91             descriptor: String,
92             ): FilterPolicyWithReason {
93         return mPolicies[getMethodKey(className, methodName, descriptor)]
94                 ?: super.getPolicyForMethod(className, methodName, descriptor)
95     }
96 
setPolicyForMethodnull97     fun setPolicyForMethod(
98             className: String,
99             methodName: String,
100             descriptor: String,
101             policy: FilterPolicyWithReason,
102             ) {
103         checkMethod(className, methodName, descriptor)
104         mPolicies[getMethodKey(className, methodName, descriptor)] = policy
105     }
106 
getRenameTonull107     override fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
108         return mRenames[getMethodKey(className, methodName, descriptor)]
109                 ?: super.getRenameTo(className, methodName, descriptor)
110     }
111 
setRenameTonull112     fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
113         checkMethod(className, methodName, descriptor)
114         checkMethod(className, toName, descriptor)
115         mRenames[getMethodKey(className, methodName, descriptor)] = toName
116     }
117 
getNativeSubstitutionClassnull118     override fun getNativeSubstitutionClass(className: String): String? {
119         return mNativeSubstitutionClasses[getClassKey(className)]
120                 ?: super.getNativeSubstitutionClass(className)
121     }
122 
setNativeSubstitutionClassnull123     fun setNativeSubstitutionClass(from: String, to: String) {
124         checkClass(from)
125 
126         // Native substitute classes may be provided from other jars, so we can't do this check.
127         // ensureClassExists(to)
128         mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName()
129     }
130 
getClassLoadHooksnull131     override fun getClassLoadHooks(className: String): List<String> {
132         return addNonNullElement(super.getClassLoadHooks(className),
133             mClassLoadHooks[getClassKey(className)])
134     }
135 
setClassLoadHooknull136     fun setClassLoadHook(className: String, methodName: String) {
137         mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName()
138     }
139 }