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