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.visitors 17 18 import com.android.hoststubgen.HostStubGenErrors 19 import com.android.hoststubgen.HostStubGenStats 20 import com.android.hoststubgen.asm.ClassNodes 21 import com.android.hoststubgen.asm.UnifiedVisitor 22 import com.android.hoststubgen.asm.getPackageNameFromFullClassName 23 import com.android.hoststubgen.filters.FilterPolicy 24 import com.android.hoststubgen.filters.FilterPolicyWithReason 25 import com.android.hoststubgen.filters.OutputFilter 26 import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep 27 import com.android.hoststubgen.log 28 import org.objectweb.asm.ClassVisitor 29 import org.objectweb.asm.FieldVisitor 30 import org.objectweb.asm.MethodVisitor 31 import org.objectweb.asm.Opcodes 32 33 const val OPCODE_VERSION = Opcodes.ASM9 34 35 abstract class BaseAdapter( 36 protected val classes: ClassNodes, 37 nextVisitor: ClassVisitor, 38 protected val filter: OutputFilter, 39 protected val options: Options, 40 ) : ClassVisitor(OPCODE_VERSION, nextVisitor) { 41 42 /** 43 * Options to control the behavior. 44 */ 45 data class Options( 46 val errors: HostStubGenErrors, 47 val stats: HostStubGenStats?, 48 val deleteClassFinals: Boolean, 49 val deleteMethodFinals: Boolean, 50 // We don't remove finals from fields, because final fields have a stronger memory 51 // guarantee than non-final fields, see: 52 // https://docs.oracle.com/javase/specs/jls/se22/html/jls-17.html#jls-17.5 53 // i.e. changing a final field to non-final _could_ result in different behavior. 54 ) 55 56 protected lateinit var currentPackageName: String 57 protected lateinit var currentClassName: String 58 protected var redirectionClass: String? = null 59 protected lateinit var classPolicy: FilterPolicyWithReason 60 isEnumnull61 private fun isEnum(access: Int): Boolean { 62 return (access and Opcodes.ACC_ENUM) != 0 63 } 64 modifyClassAccessnull65 protected fun modifyClassAccess(access: Int): Int { 66 if (options.deleteClassFinals && !isEnum(access)) { 67 return access and Opcodes.ACC_FINAL.inv() 68 } 69 return access 70 } 71 modifyMethodAccessnull72 protected fun modifyMethodAccess(access: Int): Int { 73 if (options.deleteMethodFinals) { 74 return access and Opcodes.ACC_FINAL.inv() 75 } 76 return access 77 } 78 visitnull79 override fun visit( 80 version: Int, 81 origAccess: Int, 82 name: String, 83 signature: String?, 84 superName: String?, 85 interfaces: Array<String>, 86 ) { 87 val access = modifyClassAccess(origAccess) 88 super.visit(version, access, name, signature, superName, interfaces) 89 currentClassName = name 90 currentPackageName = getPackageNameFromFullClassName(name) 91 classPolicy = filter.getPolicyForClass(currentClassName) 92 redirectionClass = filter.getRedirectionClass(currentClassName) 93 94 log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName) 95 log.indent() 96 log.v("Emitting class: %s", name) 97 log.indent() 98 99 // Inject annotations to generated classes. 100 UnifiedVisitor.on(this).visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true) 101 } 102 visitEndnull103 override fun visitEnd() { 104 log.unindent() 105 log.unindent() 106 super.visitEnd() 107 } 108 109 var skipMemberModificationNestCount = 0 110 111 /** 112 * This method allows writing class members without any modifications. 113 */ writeRawMembersnull114 protected inline fun writeRawMembers(callback: () -> Unit) { 115 skipMemberModificationNestCount++ 116 try { 117 callback() 118 } finally { 119 skipMemberModificationNestCount-- 120 } 121 } 122 visitFieldnull123 override fun visitField( 124 access: Int, 125 name: String, 126 descriptor: String, 127 signature: String?, 128 value: Any?, 129 ): FieldVisitor? { 130 if (skipMemberModificationNestCount > 0) { 131 return super.visitField(access, name, descriptor, signature, value) 132 } 133 val policy = filter.getPolicyForField(currentClassName, name) 134 log.d("visitField: %s %s [%x] Policy: %s", name, descriptor, access, policy) 135 136 log.withIndent { 137 if (policy.policy == FilterPolicy.Remove) { 138 log.d("Removing %s %s", name, policy) 139 return null 140 } 141 142 log.v("Emitting field: %s %s %s", name, descriptor, policy) 143 val ret = super.visitField(access, name, descriptor, signature, value) 144 145 UnifiedVisitor.on(ret) 146 .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true) 147 148 return ret 149 } 150 } 151 visitMethodnull152 final override fun visitMethod( 153 origAccess: Int, 154 name: String, 155 descriptor: String, 156 signature: String?, 157 exceptions: Array<String>?, 158 ): MethodVisitor? { 159 val access = modifyMethodAccess(origAccess) 160 if (skipMemberModificationNestCount > 0) { 161 return super.visitMethod(access, name, descriptor, signature, exceptions) 162 } 163 val p = filter.getPolicyForMethod(currentClassName, name, descriptor) 164 log.d("visitMethod: %s%s [%x] [%s] Policy: %s", name, descriptor, access, signature, p) 165 options.stats?.onVisitPolicyForMethod(currentClassName, name, descriptor, p, access) 166 167 log.withIndent { 168 // If it's a substitute-from method, then skip (== remove). 169 // Instead of this method, we rename the substitute-to method with the original 170 // name, in the "Maybe rename the method" part below. 171 val policy = filter.getPolicyForMethod(currentClassName, name, descriptor) 172 if (policy.policy == FilterPolicy.Substitute) { 173 log.d("Skipping %s%s %s", name, descriptor, policy) 174 return null 175 } 176 if (p.policy == FilterPolicy.Remove) { 177 log.d("Removing %s%s %s", name, descriptor, policy) 178 return null 179 } 180 181 var newAccess = access 182 183 // Maybe rename the method. 184 val newName: String 185 val renameTo = filter.getRenameTo(currentClassName, name, descriptor) 186 if (renameTo != null) { 187 newName = renameTo 188 189 // It's confusing, but here, `newName` is the original method name 190 // (the one with the @substitute/replace annotation). 191 // `name` is the name of the method we're currently visiting, so it's usually a 192 // "...$ravewnwood" name. 193 newAccess = checkSubstitutionMethodCompatibility( 194 classes, currentClassName, newName, name, descriptor, options.errors 195 ) 196 if (newAccess == NOT_COMPATIBLE) { 197 return null 198 } 199 newAccess = modifyMethodAccess(newAccess) 200 201 log.v( 202 "Emitting %s.%s%s as %s %s", currentClassName, name, descriptor, 203 newName, policy 204 ) 205 } else { 206 log.v("Emitting method: %s%s %s", name, descriptor, policy) 207 newName = name 208 } 209 210 // Let subclass update the flag. 211 // But note, we only use it when calling the super's method, 212 // but not for visitMethodInner(), because when subclass wants to change access, 213 // it can do so inside visitMethodInner(). 214 newAccess = updateAccessFlags(newAccess, name, descriptor, policy.policy) 215 216 val ret = visitMethodInner( 217 access, newName, descriptor, signature, exceptions, policy, 218 renameTo != null, 219 super.visitMethod(newAccess, newName, descriptor, signature, exceptions) 220 ) 221 222 ret?.let { 223 UnifiedVisitor.on(ret) 224 .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true) 225 } 226 227 return ret 228 } 229 } 230 updateAccessFlagsnull231 open fun updateAccessFlags( 232 access: Int, 233 name: String, 234 descriptor: String, 235 policy: FilterPolicy, 236 ): Int { 237 return access 238 } 239 visitMethodInnernull240 abstract fun visitMethodInner( 241 access: Int, 242 name: String, 243 descriptor: String, 244 signature: String?, 245 exceptions: Array<String>?, 246 policy: FilterPolicyWithReason, 247 substituted: Boolean, 248 superVisitor: MethodVisitor?, 249 ): MethodVisitor? 250 } 251