• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.asm.toJvmClassName
23 import com.android.hoststubgen.log
24 
25 // TODO: Validate all input names.
26 
27 class InMemoryOutputFilter(
28     private val classes: ClassNodes,
29     fallback: OutputFilter,
30 ) : DelegatingFilter(fallback) {
31     private val mPolicies = mutableMapOf<String, FilterPolicyWithReason>()
32     private val mRenames = mutableMapOf<String, String>()
33     private val mRedirectionClasses = mutableMapOf<String, String>()
34     private val mClassLoadHooks = mutableMapOf<String, String>()
35     private val mMethodCallReplaceSpecs = mutableListOf<MethodCallReplaceSpec>()
36     private val mTypeRenameSpecs = mutableListOf<TypeRenameSpec>()
37 
getClassKeynull38     private fun getClassKey(className: String): String {
39         return className.toHumanReadableClassName()
40     }
41 
getFieldKeynull42     private fun getFieldKey(className: String, fieldName: String): String {
43         return getClassKey(className) + "." + fieldName
44     }
45 
getMethodKeynull46     private fun getMethodKey(className: String, methodName: String, signature: String): String {
47         return getClassKey(className) + "." + methodName + ";" + signature
48     }
49 
checkClassnull50     private fun checkClass(className: String) {
51         if (classes.findClass(className) == null) {
52             log.w("Unknown class $className")
53         }
54     }
55 
checkFieldnull56     private fun checkField(className: String, fieldName: String) {
57         if (classes.findField(className, fieldName) == null) {
58             log.w("Unknown field $className.$fieldName")
59         }
60     }
61 
checkMethodnull62     private fun checkMethod(
63         className: String,
64         methodName: String,
65         descriptor: String
66     ) {
67         if (descriptor == "*") {
68             return
69         }
70         if (classes.findMethod(className, methodName, descriptor) == null) {
71             log.w("Unknown method $className.$methodName$descriptor")
72         }
73     }
74 
getPolicyForClassnull75     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
76         return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
77     }
78 
setPolicyForClassnull79     fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) {
80         checkClass(className)
81         mPolicies[getClassKey(className)] = policy
82     }
83 
getPolicyForFieldnull84     override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
85         return mPolicies[getFieldKey(className, fieldName)]
86             ?: super.getPolicyForField(className, fieldName)
87     }
88 
setPolicyForFieldnull89     fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
90         checkField(className, fieldName)
91         mPolicies[getFieldKey(className, fieldName)] = policy
92     }
93 
getPolicyForMethodnull94     override fun getPolicyForMethod(
95         className: String,
96         methodName: String,
97         descriptor: String,
98     ): FilterPolicyWithReason {
99         return mPolicies[getMethodKey(className, methodName, descriptor)]
100             ?: mPolicies[getMethodKey(className, methodName, "*")]
101             ?: super.getPolicyForMethod(className, methodName, descriptor)
102     }
103 
setPolicyForMethodnull104     fun setPolicyForMethod(
105         className: String,
106         methodName: String,
107         descriptor: String,
108         policy: FilterPolicyWithReason,
109     ) {
110         checkMethod(className, methodName, descriptor)
111         mPolicies[getMethodKey(className, methodName, descriptor)] = policy
112     }
113 
getRenameTonull114     override fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
115         return mRenames[getMethodKey(className, methodName, descriptor)]
116             ?: mRenames[getMethodKey(className, methodName, "*")]
117             ?: super.getRenameTo(className, methodName, descriptor)
118     }
119 
setRenameTonull120     fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
121         checkMethod(className, methodName, descriptor)
122         checkMethod(className, toName, descriptor)
123         mRenames[getMethodKey(className, methodName, descriptor)] = toName
124     }
125 
getRedirectionClassnull126     override fun getRedirectionClass(className: String): String? {
127         return mRedirectionClasses[getClassKey(className)]
128             ?: super.getRedirectionClass(className)
129     }
130 
setRedirectionClassnull131     fun setRedirectionClass(from: String, to: String) {
132         checkClass(from)
133 
134         // Redirection classes may be provided from other jars, so we can't do this check.
135         // ensureClassExists(to)
136         mRedirectionClasses[getClassKey(from)] = to.toJvmClassName()
137     }
138 
getClassLoadHooksnull139     override fun getClassLoadHooks(className: String): List<String> {
140         return addNonNullElement(
141             super.getClassLoadHooks(className),
142             mClassLoadHooks[getClassKey(className)]
143         )
144     }
145 
setClassLoadHooknull146     fun setClassLoadHook(className: String, methodName: String) {
147         mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName()
148     }
149 
hasAnyMethodCallReplacenull150     override fun hasAnyMethodCallReplace(): Boolean {
151         return mMethodCallReplaceSpecs.isNotEmpty() || super.hasAnyMethodCallReplace()
152     }
153 
getMethodCallReplaceTonull154     override fun getMethodCallReplaceTo(
155         className: String,
156         methodName: String,
157         descriptor: String,
158     ): MethodReplaceTarget? {
159         // Maybe use 'Tri' if we end up having too many replacements.
160         mMethodCallReplaceSpecs.forEach {
161             if (className == it.fromClass &&
162                 methodName == it.fromMethod
163             ) {
164                 if (it.fromDescriptor == "*" || descriptor == it.fromDescriptor) {
165                     return MethodReplaceTarget(it.toClass, it.toMethod)
166                 }
167             }
168         }
169         return super.getMethodCallReplaceTo(className, methodName, descriptor)
170     }
171 
setMethodCallReplaceSpecnull172     fun setMethodCallReplaceSpec(spec: MethodCallReplaceSpec) {
173         mMethodCallReplaceSpecs.add(spec)
174     }
175 
remapTypenull176     override fun remapType(className: String): String? {
177         mTypeRenameSpecs.forEach {
178             if (it.typeInternalNamePattern.matcher(className).matches()) {
179                 return it.typeInternalNamePrefix + className
180             }
181         }
182         return super.remapType(className)
183     }
184 
setRemapTypeSpecnull185     fun setRemapTypeSpec(spec: TypeRenameSpec) {
186         mTypeRenameSpecs.add(spec)
187     }
188 }
189