xref: /aosp_15_r20/external/perfmark/agent/src/main/java/io/perfmark/agent/MethodVisitorRecorder.java (revision 27e8546d0ef5f99cf83d5252272c7dd38d18d29a)
1 /*
2  * Copyright 2021 Carl Mastrangelo
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 
17 package io.perfmark.agent;
18 
19 import org.objectweb.asm.AnnotationVisitor;
20 import org.objectweb.asm.Attribute;
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  * This class records the "header" portion of the method visitor.
28  */
29 class MethodVisitorRecorder extends MethodVisitor {
30 
31   private final int VISIT_PARAMETER = 1;
32   private final int VISIT_ANNOTATION_DEFAULT = 2;
33 
34   private final int ANNOTATION_VISIT = 3;
35   private final int ANNOTATION_VISIT_ENUM = 4;
36   private final int ANNOTATION_VISIT_ANNOTATION = 5;
37   private final int ANNOTATION_VISIT_ARRAY = 6;
38   private final int ANNOTATION_VISIT_END = 7;
39 
40   private final int VISIT_ANNOTATION = 8;
41   private final int VISIT_ANNOTABLE_PARAMETER_COUNT = 9;
42   private final int VISIT_PARAMETER_ANNOTATION = 10;
43   private final int VISIT_TYPE_ANNOTATION = 11;
44   private final int VISIT_ATTRIBUTE = 12;
45 
46   private final AnnotationVisitorRecorder annotationVisitorRecorder = new AnnotationVisitorRecorder(null);
47 
48   private int opsWidx;
49   private int intsWidx;
50   private int stringsWidx;
51   private int objectsWidx;
52   private int booleansWidx;
53 
54   private int[] ops = new int[0];
55   private String[] strings = new String[0];
56   private int[] ints = new int[0];
57   private Object[] objects = new Object[0];
58   private boolean[] booleans = new boolean[0];
59 
60   int firstLine = -1;
61   int lastLine = -1;
62 
MethodVisitorRecorder(MethodVisitor delegate)63   MethodVisitorRecorder(MethodVisitor delegate) {
64     // Have to pin to a specific version, since the method invocations may be different in a later release
65     super(Opcodes.ASM9, delegate);
66   }
67 
68   /*
69    * Docs for Method Visitor Say:
70    *
71    * A visitor to visit a Java method. The methods of this class must be called in the following order:
72    * ( visitParameter )*
73    * [ visitAnnotationDefault ]
74    * ( visitAnnotation | visitAnnotableParameterCount | visitParameterAnnotation visitTypeAnnotation | visitAttribute )*
75    * [ visitCode (
76    *     visitFrame | visit<i>X</i>Insn | visitLabel | visitInsnAnnotation | visitTryCatchBlock
77    *         | visitTryCatchAnnotation | visitLocalVariable | visitLocalVariableAnnotation | visitLineNumber )*
78    *     visitMaxs ]
79    * visitEnd.
80    *
81    *
82    * In addition, the visit<i>X</i>Insn and visitLabel methods must be called in the sequential order of the bytecode
83    * instructions of the visited code, visitInsnAnnotation must be called after the annotated instruction,
84    *  visitTryCatchBlock must be called before the labels passed as arguments have been visited,
85    * visitTryCatchBlockAnnotation must be called after the corresponding try catch block has been visited, and the
86    * visitLocalVariable, visitLocalVariableAnnotation and visitLineNumber methods must be called after the labels passed
87    * as arguments have been visited.
88    */
89 
90   @Override
visitParameter(String name, int access)91   public final void visitParameter(String name, int access) {
92     addOp(VISIT_PARAMETER);
93     addString(name);
94     addInt(access);
95     super.visitParameter(name, access);
96   }
97 
98   @Override
visitAnnotationDefault()99   public final AnnotationVisitor visitAnnotationDefault() {
100     addOp(VISIT_ANNOTATION_DEFAULT);
101     if (mv == null) {
102       return annotationVisitorRecorder;
103     }
104     return new AnnotationVisitorRecorder(super.visitAnnotationDefault());
105   }
106 
107   @Override
visitAnnotation(String descriptor, boolean visible)108   public final AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
109     addOp(VISIT_ANNOTATION);
110     addString(descriptor);
111     addBoolean(visible);
112     if (mv == null) {
113       return annotationVisitorRecorder;
114     }
115     return new AnnotationVisitorRecorder(super.visitAnnotation(descriptor, visible));
116   }
117 
118   @Override
visitAnnotableParameterCount(int parameterCount, boolean visible)119   public final void visitAnnotableParameterCount(int parameterCount, boolean visible) {
120     addOp(VISIT_ANNOTABLE_PARAMETER_COUNT);
121     addInt(parameterCount);
122     addBoolean(visible);
123     super.visitAnnotableParameterCount(parameterCount, visible);
124   }
125 
126   @Override
visitParameterAnnotation(int parameter, String descriptor, boolean visible)127   public final AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) {
128     addOp(VISIT_PARAMETER_ANNOTATION);
129     addInt(parameter);
130     addString(descriptor);
131     addBoolean(visible);
132     if (mv == null) {
133       return annotationVisitorRecorder;
134     }
135     return new AnnotationVisitorRecorder(super.visitParameterAnnotation(parameter, descriptor, visible));
136   }
137 
138   @Override
visitTypeAnnotation( int typeRef, TypePath typePath, String descriptor, boolean visible)139   public final AnnotationVisitor visitTypeAnnotation(
140       int typeRef, TypePath typePath, String descriptor, boolean visible) {
141     addOp(VISIT_TYPE_ANNOTATION);
142     addInt(typeRef);
143     addObject(typePath);
144     addString(descriptor);
145     addBoolean(visible);
146     if (mv == null) {
147       return annotationVisitorRecorder;
148     }
149     return new AnnotationVisitorRecorder(super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
150   }
151 
152   @Override
visitAttribute(Attribute attribute)153   public final void visitAttribute(Attribute attribute) {
154     addOp(VISIT_ATTRIBUTE);
155     addObject(attribute);
156     super.visitAttribute(attribute);
157   }
158 
159   @Override
visitLineNumber(int line, Label start)160   public final void visitLineNumber(int line, Label start) {
161     if (firstLine == -1) {
162       firstLine = line;
163     }
164     lastLine = line;
165     super.visitLineNumber(line, start);
166   }
167 
replay()168   final void replay() {
169     if (mv != null) {
170       replay(mv);
171     }
172   }
173 
replay(MethodVisitor delegate)174   final void replay(MethodVisitor delegate) {
175     if (delegate == null) {
176       return;
177     }
178     int stringsRidx = 0;
179     int intsRidx = 0;
180     int objectsRidx = 0;
181     int booleansRidx = 0;
182     int annoWidx = 0;
183     AnnotationVisitor[] visitorStack = new AnnotationVisitor[0];
184     for (int opsRidx = 0; opsRidx < opsWidx; opsRidx++) {
185       int op = ops[opsRidx];
186       switch (op) {
187         case VISIT_PARAMETER: {
188           String name = getString(stringsRidx++);
189           int access = getInt(intsRidx++);
190           delegate.visitParameter(name, access);
191           break;
192         }
193         case VISIT_ANNOTATION_DEFAULT: {
194           AnnotationVisitor visitor = delegate.visitAnnotationDefault();
195           visitorStack = addAnnotationVisitor(visitorStack, annoWidx++, visitor);
196           break;
197         }
198         case ANNOTATION_VISIT: {
199           AnnotationVisitor currentVisitor = visitorStack[annoWidx - 1];
200           String name = getString(stringsRidx++);
201           Object value = getObject(objectsRidx++);
202           if (currentVisitor != null) {
203             currentVisitor.visit(name, value);
204           }
205           break;
206         }
207         case ANNOTATION_VISIT_ENUM: {
208           AnnotationVisitor currentVisitor = visitorStack[annoWidx - 1];
209           String name = getString(stringsRidx++);
210           String descriptor = getString(stringsRidx++);
211           String value = getString(stringsRidx++);
212           if (currentVisitor != null) {
213             currentVisitor.visitEnum(name, descriptor, value);
214           }
215           break;
216         }
217         case ANNOTATION_VISIT_ANNOTATION: {
218           AnnotationVisitor currentVisitor = visitorStack[annoWidx - 1];
219           String name = getString(stringsRidx++);
220           String descriptor = getString(stringsRidx++);
221           AnnotationVisitor newVisitor = null;
222           if (currentVisitor != null) {
223             newVisitor = currentVisitor.visitAnnotation(name, descriptor);
224           }
225           visitorStack = addAnnotationVisitor(visitorStack, annoWidx++, newVisitor);
226           break;
227         }
228         case ANNOTATION_VISIT_ARRAY: {
229           AnnotationVisitor currentVisitor = visitorStack[annoWidx - 1];
230           String name = getString(stringsRidx++);
231           AnnotationVisitor newVisitor = null;
232           if (currentVisitor != null) {
233             newVisitor = currentVisitor.visitArray(name);
234           }
235           visitorStack = addAnnotationVisitor(visitorStack, annoWidx++, newVisitor);
236           break;
237         }
238         case ANNOTATION_VISIT_END: {
239           AnnotationVisitor currentVisitor = visitorStack[annoWidx - 1];
240           visitorStack[--annoWidx] = null;
241           if (currentVisitor != null) {
242             currentVisitor.visitEnd();
243           }
244           break;
245         }
246         case VISIT_ANNOTATION: {
247           String descriptor = getString(stringsRidx++);
248           boolean visible = getBoolean(booleansRidx++);
249           AnnotationVisitor newVisitor = delegate.visitAnnotation(descriptor, visible);
250           visitorStack = addAnnotationVisitor(visitorStack, annoWidx++, newVisitor);
251           break;
252         }
253         case VISIT_ANNOTABLE_PARAMETER_COUNT: {
254           int parameterCount = getInt(intsRidx++);
255           boolean visible = getBoolean(booleansRidx++);
256           delegate.visitAnnotableParameterCount(parameterCount, visible);
257           break;
258         }
259         case VISIT_PARAMETER_ANNOTATION: {
260           int parameter = getInt(intsRidx++);
261           String descriptor = getString(stringsRidx++);
262           boolean visible = getBoolean(booleansRidx++);
263           AnnotationVisitor newVisitor = delegate.visitParameterAnnotation(parameter, descriptor, visible);
264           visitorStack = addAnnotationVisitor(visitorStack, annoWidx++, newVisitor);
265           break;
266         }
267         case VISIT_TYPE_ANNOTATION: {
268           // (int typeRef, TypePath typePath, String descriptor, boolean visible)
269           int typeRef = getInt(intsRidx++);
270           TypePath typePath = (TypePath) getObject(objectsRidx++);
271           String descriptor = getString(stringsRidx++);
272           boolean visible = getBoolean(booleansRidx++);
273           AnnotationVisitor newVisitor = delegate.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
274           visitorStack = addAnnotationVisitor(visitorStack, annoWidx++, newVisitor);
275           break;
276         }
277         case VISIT_ATTRIBUTE: {
278           Attribute attribute = (Attribute) getObject(objectsRidx++);
279           delegate.visitAttribute(attribute);
280           break;
281         }
282         default:
283           throw new AssertionError("Bad op " + op);
284       }
285     }
286   }
287 
addOp(int op)288   private void addOp(int op) {
289     ops = addInt(ops, opsWidx++, op);
290   }
291 
addInt(int value)292   private void addInt(int value) {
293     ints = addInt(ints, intsWidx++, value);
294   }
295 
addInt(int[] dest, int pos, int value)296   private static int[] addInt(int[] dest, int pos, int value) {
297     if (dest.length == pos){
298       int[] newDest = new int[1 + dest.length * 2];
299       System.arraycopy(dest, 0, newDest, 0, dest.length);
300       dest = newDest;
301     }
302     dest[pos] = value;
303     return dest;
304   }
305 
addString(String value)306   private void addString(String value) {
307     strings = addString(strings, stringsWidx++, value);
308   }
309 
addString(String[] dest, int pos, String value)310   private static String[] addString(String[] dest, int pos, String value) {
311     if (dest.length == pos){
312       String[] newDest = new String[1 + dest.length * 2];
313       System.arraycopy(dest, 0, newDest, 0, dest.length);
314       dest = newDest;
315     }
316     dest[pos] = value;
317     return dest;
318   }
319 
addObject(Object value)320   private void addObject(Object value) {
321     objects = addObject(objects, objectsWidx++, value);
322   }
323 
addObject(Object[] dest, int pos, Object value)324   private static Object[] addObject(Object[] dest, int pos, Object value) {
325     if (dest.length == pos){
326       Object[] newDest = new Object[1 + dest.length * 2];
327       System.arraycopy(dest, 0, newDest, 0, dest.length);
328       dest = newDest;
329     }
330     dest[pos] = value;
331     return dest;
332   }
333 
addBoolean(boolean value)334   private void addBoolean(boolean value) {
335     booleans = addBoolean(booleans, booleansWidx++, value);
336   }
337 
addBoolean(boolean[] dest, int pos, boolean value)338   private static boolean[] addBoolean(boolean[] dest, int pos, boolean value) {
339     if (dest.length == pos){
340       boolean[] newDest = new boolean[1 + dest.length * 2];
341       System.arraycopy(dest, 0, newDest, 0, dest.length);
342       dest = newDest;
343     }
344     dest[pos] = value;
345     return dest;
346   }
347 
addAnnotationVisitor(AnnotationVisitor[] dest, int pos, AnnotationVisitor value)348   private static AnnotationVisitor[] addAnnotationVisitor(AnnotationVisitor[] dest, int pos, AnnotationVisitor value) {
349     if (dest.length == pos){
350       AnnotationVisitor[] newDest = new AnnotationVisitor[1 + dest.length * 2];
351       System.arraycopy(dest, 0, newDest, 0, dest.length);
352       dest = newDest;
353     }
354     dest[pos] = value;
355     return dest;
356   }
357 
getInt(int ridx)358   private int getInt(int ridx) {
359     assert ridx < intsWidx;
360     return ints[ridx];
361   }
362 
363   private String getString(int ridx) {
364     assert ridx < stringsWidx;
365     return strings[ridx];
366   }
367 
368   private Object getObject(int ridx) {
369     assert ridx < objectsWidx;
370     return objects[ridx];
371   }
372 
373   private boolean getBoolean(int ridx) {
374     assert ridx < booleansWidx;
375     return booleans[ridx];
376   }
377 
378   private final class AnnotationVisitorRecorder extends AnnotationVisitor {
379 
380     AnnotationVisitorRecorder(AnnotationVisitor delegate) {
381       super(MethodVisitorRecorder.this.api, delegate);
382     }
383 
384     /*
385      * Docs for AnnotationVisitor say
386      *
387      * A visitor to visit a Java annotation. The methods of this class must be called in the following order:
388      * ( visit | visitEnum | visitAnnotation | visitArray )* visitEnd.
389      */
390 
391     @Override
392     public void visit(String name, Object value) {
393       addOp(ANNOTATION_VISIT);
394       addString(name);
395       addObject(value);
396       super.visit(name, value);
397     }
398 
399     @Override
400     public void visitEnum(String name, String descriptor, String value) {
401       addOp(ANNOTATION_VISIT_ENUM);
402       addString(name);
403       addString(descriptor);
404       addString(value);
405       super.visitEnum(name, descriptor, value);
406     }
407 
408     @Override
409     public AnnotationVisitor visitAnnotation(String name, String descriptor) {
410       addOp(ANNOTATION_VISIT_ANNOTATION);
411       addString(name);
412       addString(descriptor);
413       if (av == null) {
414         return this;
415       }
416       return new AnnotationVisitorRecorder(super.visitAnnotation(name, descriptor));
417     }
418 
419     @Override
420     public AnnotationVisitor visitArray(String name) {
421       addOp(ANNOTATION_VISIT_ARRAY);
422       addString(name);
423       if (av == null) {
424         return this;
425       }
426       return new AnnotationVisitorRecorder(super.visitArray(name));
427     }
428 
429     @Override
430     public void visitEnd() {
431       addOp(ANNOTATION_VISIT_END);
432       super.visitEnd();
433     }
434   }
435 }
436