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 org.objectweb.asm.AnnotationVisitor
19 import org.objectweb.asm.Attribute
20 import org.objectweb.asm.Handle
21 import org.objectweb.asm.Label
22 import org.objectweb.asm.MethodVisitor
23 import org.objectweb.asm.Opcodes
24 import org.objectweb.asm.TypePath
25 
26 /**
27  * A method visitor that removes everything from method body.
28  *
29  * To inject a method body, override [visitCode] and create the opcodes there.
30  */
31 abstract class BodyReplacingMethodVisitor(
32     access: Int,
33     name: String,
34     descriptor: String,
35     signature: String?,
36     exceptions: Array<String>?,
37     next: MethodVisitor?,
38 ) : MethodVisitor(OPCODE_VERSION, next) {
39     val isVoid: Boolean
40     val isStatic: Boolean
41 
42     init {
43         isVoid = descriptor.endsWith(")V")
44         isStatic = access and Opcodes.ACC_STATIC != 0
45     }
46 
47     // Following methods are for things that we need to keep.
48     // Since they're all calling the super method, we can just remove them, but we keep them
49     // just to clarify what we're keeping.
50 
visitParameternull51     final override fun visitParameter(
52             name: String?,
53             access: Int
54     ) {
55         super.visitParameter(name, access)
56     }
57 
visitAnnotationDefaultnull58     final override fun visitAnnotationDefault(): AnnotationVisitor? {
59         return super.visitAnnotationDefault()
60     }
61 
visitAnnotationnull62     final override fun visitAnnotation(
63             descriptor: String?,
64             visible: Boolean
65     ): AnnotationVisitor? {
66         return super.visitAnnotation(descriptor, visible)
67     }
68 
visitTypeAnnotationnull69     final override fun visitTypeAnnotation(
70         typeRef: Int,
71         typePath: TypePath?,
72         descriptor: String?,
73         visible: Boolean
74     ): AnnotationVisitor? {
75         return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)
76     }
77 
visitAnnotableParameterCountnull78     final override fun visitAnnotableParameterCount(
79             parameterCount: Int,
80             visible: Boolean
81     ) {
82         super.visitAnnotableParameterCount(parameterCount, visible)
83     }
84 
visitParameterAnnotationnull85     final override fun visitParameterAnnotation(
86             parameter: Int,
87             descriptor: String?,
88             visible: Boolean
89     ): AnnotationVisitor? {
90         return super.visitParameterAnnotation(parameter, descriptor, visible)
91     }
92 
visitAttributenull93     final override fun visitAttribute(attribute: Attribute?) {
94         super.visitAttribute(attribute)
95     }
96 
visitEndnull97     override fun visitEnd() {
98         super.visitEnd()
99     }
100 
101     /**
102      * Control when to emit the code. We use this to ignore all visitXxx method calls caused by
103      * the original method, so we'll remove all the original code.
104      *
105      * Only when visitXxx methods are called from [emitNewCode], we pass-through to the base class,
106      * so the body will be generated.
107      *
108      * (See also https://asm.ow2.io/asm4-guide.pdf section 3.2.1 about the MethovVisitor
109      * call order.)
110      */
111     var emitCode = false
112 
visitCodenull113     final override fun visitCode() {
114         super.visitCode()
115 
116         try {
117             emitCode = true
118 
119             emitNewCode()
120         } finally {
121             emitCode = false
122         }
123     }
124 
125     /**
126      * Subclass must implement it and emit code, and call [visitMaxs] at the end.
127      */
emitNewCodenull128     abstract fun emitNewCode()
129 
130     final override fun visitMaxs(
131             maxStack: Int,
132             maxLocals: Int
133     ) {
134         if (emitCode) {
135             super.visitMaxs(maxStack, maxLocals)
136         }
137     }
138 
139     // Following methods are called inside a method body, and we don't want to
140     // emit any of them, so they are all no-op.
141 
visitFramenull142     final override fun visitFrame(
143             type: Int,
144             numLocal: Int,
145             local: Array<out Any>?,
146             numStack: Int,
147             stack: Array<out Any>?
148     ) {
149         if (emitCode) {
150             super.visitFrame(type, numLocal, local, numStack, stack)
151         }
152     }
153 
visitInsnnull154     final override fun visitInsn(opcode: Int) {
155         if (emitCode) {
156             super.visitInsn(opcode)
157         }
158     }
159 
visitIntInsnnull160     final override fun visitIntInsn(
161             opcode: Int,
162             operand: Int
163     ) {
164         if (emitCode) {
165             super.visitIntInsn(opcode, operand)
166         }
167     }
168 
visitVarInsnnull169     final override fun visitVarInsn(
170             opcode: Int,
171             varIndex: Int
172     ) {
173         if (emitCode) {
174             super.visitVarInsn(opcode, varIndex)
175         }
176     }
177 
visitTypeInsnnull178     final override fun visitTypeInsn(
179             opcode: Int,
180             type: String?
181     ) {
182         if (emitCode) {
183             super.visitTypeInsn(opcode, type)
184         }
185     }
186 
visitFieldInsnnull187     final override fun visitFieldInsn(
188             opcode: Int,
189             owner: String?,
190             name: String?,
191             descriptor: String?
192     ) {
193         if (emitCode) {
194             super.visitFieldInsn(opcode, owner, name, descriptor)
195         }
196     }
197 
visitMethodInsnnull198     final override fun visitMethodInsn(
199             opcode: Int,
200             owner: String?,
201             name: String?,
202             descriptor: String?,
203             isInterface: Boolean
204     ) {
205         if (emitCode) {
206             super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
207         }
208     }
209 
visitInvokeDynamicInsnnull210     final override fun visitInvokeDynamicInsn(
211             name: String?,
212             descriptor: String?,
213             bootstrapMethodHandle: Handle?,
214             vararg bootstrapMethodArguments: Any?
215     ) {
216         if (emitCode) {
217             super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle,
218                     *bootstrapMethodArguments)
219         }
220     }
221 
visitJumpInsnnull222     final override fun visitJumpInsn(
223             opcode: Int,
224             label: Label?
225     ) {
226         if (emitCode) {
227             super.visitJumpInsn(opcode, label)
228         }
229     }
230 
visitLabelnull231     final override fun visitLabel(label: Label?) {
232         if (emitCode) {
233             super.visitLabel(label)
234         }
235     }
236 
visitLdcInsnnull237     final override fun visitLdcInsn(value: Any?) {
238         if (emitCode) {
239             super.visitLdcInsn(value)
240         }
241     }
242 
visitIincInsnnull243     final override fun visitIincInsn(
244             varIndex: Int,
245             increment: Int
246     ) {
247         if (emitCode) {
248             super.visitIincInsn(varIndex, increment)
249         }
250     }
251 
visitTableSwitchInsnnull252     final override fun visitTableSwitchInsn(
253             min: Int,
254             max: Int,
255             dflt: Label?,
256             vararg labels: Label?
257     ) {
258         if (emitCode) {
259             super.visitTableSwitchInsn(min, max, dflt, *labels)
260         }
261     }
262 
visitLookupSwitchInsnnull263     final override fun visitLookupSwitchInsn(
264             dflt: Label?,
265             keys: IntArray?,
266             labels: Array<out Label>?
267     ) {
268         if (emitCode) {
269             super.visitLookupSwitchInsn(dflt, keys, labels)
270         }
271     }
272 
visitMultiANewArrayInsnnull273     final override fun visitMultiANewArrayInsn(
274             descriptor: String?,
275             numDimensions: Int
276     ) {
277         if (emitCode) {
278             super.visitMultiANewArrayInsn(descriptor, numDimensions)
279         }
280     }
281 
visitInsnAnnotationnull282     final override fun visitInsnAnnotation(
283             typeRef: Int,
284             typePath: TypePath?,
285             descriptor: String?,
286             visible: Boolean
287     ): AnnotationVisitor? {
288         if (emitCode) {
289             return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)
290         }
291         return null
292     }
293 
visitTryCatchBlocknull294     final override fun visitTryCatchBlock(
295             start: Label?,
296             end: Label?,
297             handler: Label?,
298             type: String?
299     ) {
300         if (emitCode) {
301             super.visitTryCatchBlock(start, end, handler, type)
302         }
303     }
304 
visitTryCatchAnnotationnull305     final override fun visitTryCatchAnnotation(
306             typeRef: Int,
307             typePath: TypePath?,
308             descriptor: String?,
309             visible: Boolean
310     ): AnnotationVisitor? {
311         if (emitCode) {
312             return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)
313         }
314         return null
315     }
316 
visitLocalVariablenull317     final override fun visitLocalVariable(
318             name: String?,
319             descriptor: String?,
320             signature: String?,
321             start: Label?,
322             end: Label?,
323             index: Int
324     ) {
325         if (emitCode) {
326             super.visitLocalVariable(name, descriptor, signature, start, end, index)
327         }
328     }
329 
visitLocalVariableAnnotationnull330     final override fun visitLocalVariableAnnotation(
331             typeRef: Int,
332             typePath: TypePath?,
333             start: Array<out Label>?,
334             end: Array<out Label>?,
335             index: IntArray?,
336             descriptor: String?,
337             visible: Boolean
338     ): AnnotationVisitor? {
339         if (emitCode) {
340             return super.visitLocalVariableAnnotation(
341                     typeRef, typePath, start, end, index, descriptor, visible)
342         }
343         return null
344     }
345 
visitLineNumbernull346     final override fun visitLineNumber(
347             line: Int,
348             start: Label?
349     ) {
350         if (emitCode) {
351             super.visitLineNumber(line, start)
352         }
353     }
354 }
355