• 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.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