1 /* 2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.internal; 27 28 import java.lang.reflect.Field; 29 import java.lang.reflect.Modifier; 30 import java.lang.reflect.Parameter; 31 import java.util.ArrayList; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Set; 35 import java.util.function.Consumer; 36 37 import jdk.internal.org.objectweb.asm.ClassReader; 38 import jdk.internal.org.objectweb.asm.ClassWriter; 39 import jdk.internal.org.objectweb.asm.Label; 40 import jdk.internal.org.objectweb.asm.MethodVisitor; 41 import jdk.internal.org.objectweb.asm.Opcodes; 42 import jdk.internal.org.objectweb.asm.Type; 43 import jdk.internal.org.objectweb.asm.commons.Method; 44 import jdk.internal.org.objectweb.asm.tree.AnnotationNode; 45 import jdk.internal.org.objectweb.asm.tree.ClassNode; 46 import jdk.internal.org.objectweb.asm.tree.FieldNode; 47 import jdk.internal.org.objectweb.asm.tree.MethodNode; 48 import jdk.jfr.Enabled; 49 import jdk.jfr.Event; 50 import jdk.jfr.Name; 51 import jdk.jfr.Registered; 52 import jdk.jfr.SettingControl; 53 import jdk.jfr.SettingDefinition; 54 import jdk.jfr.internal.handlers.EventHandler; 55 56 /** 57 * Class responsible for adding instrumentation to a subclass of {@link Event}. 58 * 59 */ 60 public final class EventInstrumentation { 61 static final class SettingInfo { 62 private String methodName; 63 private String internalSettingName; 64 private String settingDescriptor; 65 final String fieldName; 66 final int index; 67 // Used when instantiating Setting 68 SettingControl settingControl; 69 70 public SettingInfo(String fieldName, int index) { 71 this.fieldName = fieldName; 72 this.index = index; 73 } 74 } 75 76 static final class FieldInfo { 77 private final static Type STRING = Type.getType(String.class); 78 final String fieldName; 79 final String fieldDescriptor; 80 final String internalClassName; 81 82 public FieldInfo(String fieldName, String fieldDescriptor, String internalClassName) { 83 this.fieldName = fieldName; 84 this.fieldDescriptor = fieldDescriptor; 85 this.internalClassName = internalClassName; 86 } 87 88 public boolean isString() { 89 return STRING.getDescriptor().equals(fieldDescriptor); 90 } 91 } 92 93 public static final String FIELD_EVENT_THREAD = "eventThread"; 94 public static final String FIELD_STACK_TRACE = "stackTrace"; 95 public static final String FIELD_DURATION = "duration"; 96 97 static final String FIELD_EVENT_HANDLER = "eventHandler"; 98 static final String FIELD_START_TIME = "startTime"; 99 100 private static final Type ANNOTATION_TYPE_NAME = Type.getType(Name.class); 101 private static final Type ANNOTATION_TYPE_REGISTERED = Type.getType(Registered.class); 102 private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class); 103 private static final Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class); 104 private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class); 105 private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]); 106 private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]); 107 private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]); 108 private static final Method METHOD_IS_ENABLED = new Method("isEnabled", Type.BOOLEAN_TYPE, new Type[0]); 109 private static final Method METHOD_TIME_STAMP = new Method("timestamp", Type.LONG_TYPE, new Type[0]); 110 private static final Method METHOD_EVENT_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[0]); 111 private static final Method METHOD_EVENT_HANDLER_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[] { Type.LONG_TYPE }); 112 private static final Method METHOD_DURATION = new Method("duration", Type.LONG_TYPE, new Type[] { Type.LONG_TYPE }); 113 114 private final ClassNode classNode; 115 private final List<SettingInfo> settingInfos; 116 private final List<FieldInfo> fieldInfos;; 117 private final Method writeMethod; 118 private final String eventHandlerXInternalName; 119 private final String eventName; 120 private boolean guardHandlerReference; 121 private Class<?> superClass; 122 123 EventInstrumentation(Class<?> superClass, byte[] bytes, long id) { 124 this.superClass = superClass; 125 this.classNode = createClassNode(bytes); 126 this.settingInfos = buildSettingInfos(superClass, classNode); 127 this.fieldInfos = buildFieldInfos(superClass, classNode); 128 this.writeMethod = makeWriteMethod(fieldInfos); 129 this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id)); 130 String n = annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class); 131 this.eventName = n == null ? classNode.name.replace("/", ".") : n; 132 133 } 134 135 public String getClassName() { 136 return classNode.name.replace("/","."); 137 } 138 139 private ClassNode createClassNode(byte[] bytes) { 140 ClassNode classNode = new ClassNode(); 141 ClassReader classReader = new ClassReader(bytes); 142 classReader.accept(classNode, 0); 143 return classNode; 144 } 145 146 boolean isRegistered() { 147 Boolean result = annotationValue(classNode, ANNOTATION_TYPE_REGISTERED.getDescriptor(), Boolean.class); 148 if (result != null) { 149 return result.booleanValue(); 150 } 151 if (superClass != null) { 152 Registered r = superClass.getAnnotation(Registered.class); 153 if (r != null) { 154 return r.value(); 155 } 156 } 157 return true; 158 } 159 160 boolean isEnabled() { 161 Boolean result = annotationValue(classNode, ANNOTATION_TYPE_ENABLED.getDescriptor(), Boolean.class); 162 if (result != null) { 163 return result.booleanValue(); 164 } 165 if (superClass != null) { 166 Enabled e = superClass.getAnnotation(Enabled.class); 167 if (e != null) { 168 return e.value(); 169 } 170 } 171 return true; 172 } 173 174 @SuppressWarnings("unchecked") 175 private static <T> T annotationValue(ClassNode classNode, String typeDescriptor, Class<?> type) { 176 if (classNode.visibleAnnotations != null) { 177 for (AnnotationNode a : classNode.visibleAnnotations) { 178 if (typeDescriptor.equals(a.desc)) { 179 List<Object> values = a.values; 180 if (values != null && values.size() == 2) { 181 Object key = values.get(0); 182 Object value = values.get(1); 183 if (key instanceof String && value != null) { 184 if (type == value.getClass()) { 185 String keyName = (String) key; 186 if ("value".equals(keyName)) { 187 return (T) value; 188 } 189 } 190 } 191 } 192 } 193 } 194 } 195 return null; 196 } 197 198 private static List<SettingInfo> buildSettingInfos(Class<?> superClass, ClassNode classNode) { 199 Set<String> methodSet = new HashSet<>(); 200 List<SettingInfo> settingInfos = new ArrayList<>(); 201 String settingDescriptor = Type.getType(SettingDefinition.class).getDescriptor(); 202 for (MethodNode m : classNode.methods) { 203 if (m.visibleAnnotations != null) { 204 for (AnnotationNode an : m.visibleAnnotations) { 205 // We can't really validate the method at this 206 // stage. We would need to check that the parameter 207 // is an instance of SettingControl. 208 if (settingDescriptor.equals(an.desc)) { 209 Type returnType = Type.getReturnType(m.desc); 210 if (returnType.equals(Type.getType(Boolean.TYPE))) { 211 Type[] args = Type.getArgumentTypes(m.desc); 212 if (args.length == 1) { 213 Type paramType = args[0]; 214 String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size(); 215 int index = settingInfos.size(); 216 SettingInfo si = new SettingInfo(fieldName, index); 217 si.methodName = m.name; 218 si.settingDescriptor = paramType.getDescriptor(); 219 si.internalSettingName = paramType.getInternalName(); 220 methodSet.add(m.name); 221 settingInfos.add(si); 222 } 223 } 224 } 225 } 226 } 227 } 228 for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) { 229 for (java.lang.reflect.Method method : c.getDeclaredMethods()) { 230 if (!methodSet.contains(method.getName())) { 231 // skip private method in base classes 232 if (!Modifier.isPrivate(method.getModifiers())) { 233 if (method.getReturnType().equals(Boolean.TYPE)) { 234 if (method.getParameterCount() == 1) { 235 Parameter param = method.getParameters()[0]; 236 Type paramType = Type.getType(param.getType()); 237 String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size(); 238 int index = settingInfos.size(); 239 SettingInfo si = new SettingInfo(fieldName, index); 240 si.methodName = method.getName(); 241 si.settingDescriptor = paramType.getDescriptor(); 242 si.internalSettingName = paramType.getInternalName(); 243 methodSet.add(method.getName()); 244 settingInfos.add(si); 245 } 246 } 247 } 248 } 249 } 250 } 251 return settingInfos; 252 253 } 254 255 private static List<FieldInfo> buildFieldInfos(Class<?> superClass, ClassNode classNode) { 256 Set<String> fieldSet = new HashSet<>(); 257 List<FieldInfo> fieldInfos = new ArrayList<>(classNode.fields.size()); 258 // These two field are added by native as transient so they will be 259 // ignored by the loop below. 260 // The benefit of adding them manually is that we can 261 // control in which order they occur and we can add @Name, @Description 262 // in Java, instead of in native. It also means code for adding implicit 263 // fields for native can be reused by Java. 264 fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name)); 265 fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name)); 266 for (FieldNode field : classNode.fields) { 267 String className = Type.getType(field.desc).getClassName(); 268 if (!fieldSet.contains(field.name) && isValidField(field.access, className)) { 269 FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name); 270 fieldInfos.add(fi); 271 fieldSet.add(field.name); 272 } 273 } 274 for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) { 275 for (Field field : c.getDeclaredFields()) { 276 // skip private field in base classes 277 if (!Modifier.isPrivate(field.getModifiers())) { 278 if (isValidField(field.getModifiers(), field.getType().getName())) { 279 String fieldName = field.getName(); 280 if (!fieldSet.contains(fieldName)) { 281 Type fieldType = Type.getType(field.getType()); 282 String internalClassName = ASMToolkit.getInternalName(c.getName()); 283 fieldInfos.add(new FieldInfo(fieldName, fieldType.getDescriptor(), internalClassName)); 284 fieldSet.add(fieldName); 285 } 286 } 287 } 288 } 289 } 290 return fieldInfos; 291 } 292 293 public static boolean isValidField(int access, String className) { 294 if (Modifier.isTransient(access) || Modifier.isStatic(access)) { 295 return false; 296 } 297 return jdk.jfr.internal.Type.isValidJavaFieldType(className); 298 } 299 300 public byte[] buildInstrumented() { 301 makeInstrumented(); 302 return toByteArray(); 303 } 304 305 private byte[] toByteArray() { 306 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 307 classNode.accept(cw); 308 cw.visitEnd(); 309 byte[] result = cw.toByteArray(); 310 Utils.writeGeneratedASM(classNode.name, result); 311 return result; 312 } 313 314 public byte[] builUninstrumented() { 315 makeUninstrumented(); 316 return toByteArray(); 317 } 318 319 private void makeInstrumented() { 320 // MyEvent#isEnabled() 321 updateMethod(METHOD_IS_ENABLED, methodVisitor -> { 322 Label nullLabel = new Label(); 323 if (guardHandlerReference) { 324 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor()); 325 methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel); 326 } 327 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor()); 328 ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED); 329 methodVisitor.visitInsn(Opcodes.IRETURN); 330 if (guardHandlerReference) { 331 methodVisitor.visitLabel(nullLabel); 332 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 333 methodVisitor.visitInsn(Opcodes.ICONST_0); 334 methodVisitor.visitInsn(Opcodes.IRETURN); 335 } 336 }); 337 338 // MyEvent#begin() 339 updateMethod(METHOD_BEGIN, methodVisitor -> { 340 methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); 341 ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP); 342 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J"); 343 methodVisitor.visitInsn(Opcodes.RETURN); 344 }); 345 346 // MyEvent#end() 347 updateMethod(METHOD_END, methodVisitor -> { 348 methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); 349 methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); 350 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); 351 ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_DURATION); 352 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J"); 353 methodVisitor.visitInsn(Opcodes.RETURN); 354 methodVisitor.visitMaxs(0, 0); 355 }); 356 357 // MyEvent#commit() - Java event writer 358 updateMethod(METHOD_COMMIT, methodVisitor -> { 359 // if (!isEnable()) { 360 // return; 361 // } 362 methodVisitor.visitCode(); 363 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 364 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false); 365 Label l0 = new Label(); 366 methodVisitor.visitJumpInsn(Opcodes.IFNE, l0); 367 methodVisitor.visitInsn(Opcodes.RETURN); 368 methodVisitor.visitLabel(l0); 369 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 370 // if (startTime == 0) { 371 // startTime = EventWriter.timestamp(); 372 // } else { 373 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 374 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); 375 methodVisitor.visitInsn(Opcodes.LCONST_0); 376 methodVisitor.visitInsn(Opcodes.LCMP); 377 Label durationalEvent = new Label(); 378 methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent); 379 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 380 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), 381 METHOD_TIME_STAMP.getDescriptor(), false); 382 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J"); 383 Label commit = new Label(); 384 methodVisitor.visitJumpInsn(Opcodes.GOTO, commit); 385 // if (duration == 0) { 386 // duration = EventWriter.timestamp() - startTime; 387 // } 388 // } 389 methodVisitor.visitLabel(durationalEvent); 390 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 391 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 392 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); 393 methodVisitor.visitInsn(Opcodes.LCONST_0); 394 methodVisitor.visitInsn(Opcodes.LCMP); 395 methodVisitor.visitJumpInsn(Opcodes.IFNE, commit); 396 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 397 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false); 398 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 399 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J"); 400 methodVisitor.visitInsn(Opcodes.LSUB); 401 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J"); 402 methodVisitor.visitLabel(commit); 403 // if (shouldCommit()) { 404 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 405 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 406 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false); 407 Label end = new Label(); 408 // eventHandler.write(...); 409 // } 410 methodVisitor.visitJumpInsn(Opcodes.IFEQ, end); 411 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); 412 413 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); 414 for (FieldInfo fi : fieldInfos) { 415 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 416 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor); 417 } 418 419 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false); 420 methodVisitor.visitLabel(end); 421 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 422 methodVisitor.visitInsn(Opcodes.RETURN); 423 methodVisitor.visitEnd(); 424 }); 425 426 // MyEvent#shouldCommit() 427 updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> { 428 Label fail = new Label(); 429 // if (!eventHandler.shoouldCommit(duration) goto fail; 430 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); 431 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); 432 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); 433 ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT); 434 methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail); 435 for (SettingInfo si : settingInfos) { 436 // if (!settingsMethod(eventHandler.settingX)) goto fail; 437 methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); 438 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); 439 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); 440 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor()); 441 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName); 442 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), si.methodName, "(" + si.settingDescriptor + ")Z", false); 443 methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail); 444 } 445 // return true 446 methodVisitor.visitInsn(Opcodes.ICONST_1); 447 methodVisitor.visitInsn(Opcodes.IRETURN); 448 // return false 449 methodVisitor.visitLabel(fail); 450 methodVisitor.visitInsn(Opcodes.ICONST_0); 451 methodVisitor.visitInsn(Opcodes.IRETURN); 452 }); 453 } 454 455 private void makeUninstrumented() { 456 updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT); 457 updateExistingWithReturnFalse(METHOD_IS_ENABLED); 458 updateExistingWithEmptyVoidMethod(METHOD_COMMIT); 459 updateExistingWithEmptyVoidMethod(METHOD_BEGIN); 460 updateExistingWithEmptyVoidMethod(METHOD_END); 461 } 462 463 private final void updateExistingWithEmptyVoidMethod(Method voidMethod) { 464 updateMethod(voidMethod, methodVisitor -> { 465 methodVisitor.visitInsn(Opcodes.RETURN); 466 }); 467 } 468 469 private final void updateExistingWithReturnFalse(Method voidMethod) { 470 updateMethod(voidMethod, methodVisitor -> { 471 methodVisitor.visitInsn(Opcodes.ICONST_0); 472 methodVisitor.visitInsn(Opcodes.IRETURN); 473 }); 474 } 475 476 private MethodNode getMethodNode(Method method) { 477 for (MethodNode m : classNode.methods) { 478 if (m.name.equals(method.getName()) && m.desc.equals(method.getDescriptor())) { 479 return m; 480 } 481 } 482 return null; 483 } 484 485 private final void updateMethod(Method method, Consumer<MethodVisitor> code) { 486 MethodNode old = getMethodNode(method); 487 int index = classNode.methods.indexOf(old); 488 classNode.methods.remove(old); 489 MethodVisitor mv = classNode.visitMethod(old.access, old.name, old.desc, null, null); 490 mv.visitCode(); 491 code.accept(mv); 492 mv.visitMaxs(0, 0); 493 MethodNode newMethod = getMethodNode(method); 494 classNode.methods.remove(newMethod); 495 classNode.methods.add(index, newMethod); 496 } 497 498 public static Method makeWriteMethod(List<FieldInfo> fields) { 499 StringBuilder sb = new StringBuilder(); 500 sb.append("("); 501 for (FieldInfo v : fields) { 502 sb.append(v.fieldDescriptor); 503 } 504 sb.append(")V"); 505 return new Method("write", sb.toString()); 506 } 507 508 private String getInternalClassName() { 509 return classNode.name; 510 } 511 512 public List<SettingInfo> getSettingInfos() { 513 return settingInfos; 514 } 515 516 public List<FieldInfo> getFieldInfos() { 517 return fieldInfos; 518 } 519 520 public String getEventName() { 521 return eventName; 522 } 523 524 public void setGuardHandler(boolean guardHandlerReference) { 525 this.guardHandlerReference = guardHandlerReference; 526 } 527 }