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 }