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