1 /*
   2  * Copyright (c) 2016, 2018, 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 Class<? extends EventHandler> eventHandlerProxy = EventHandlerProxyCreator.proxyClass;
 101     private static final Type ANNOTATION_TYPE_NAME = Type.getType(Name.class);
 102     private static final Type ANNOTATION_TYPE_REGISTERED = Type.getType(Registered.class);
 103     private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class);
 104     private static final Type TYPE_EVENT_HANDLER = Type.getType(eventHandlerProxy);
 105     private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
 106     private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]);
 107     private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]);
 108     private static final Method METHOD_END = new Method("end", Type.VOID_TYPE, new Type[0]);
 109     private static final Method METHOD_IS_ENABLED = new Method("isEnabled", Type.BOOLEAN_TYPE, new Type[0]);
 110     private static final Method METHOD_TIME_STAMP = new Method("timestamp", Type.LONG_TYPE, new Type[0]);
 111     private static final Method METHOD_EVENT_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[0]);
 112     private static final Method METHOD_EVENT_HANDLER_SHOULD_COMMIT = new Method("shouldCommit", Type.BOOLEAN_TYPE, new Type[] { Type.LONG_TYPE });
 113     private static final Method METHOD_DURATION = new Method("duration", Type.LONG_TYPE, new Type[] { Type.LONG_TYPE });
 114 
 115     private final ClassNode classNode;
 116     private final List<SettingInfo> settingInfos;
 117     private final List<FieldInfo> fieldInfos;;
 118     private final Method writeMethod;
 119     private final String eventHandlerXInternalName;
 120     private final String eventName;
 121     private boolean guardHandlerReference;
 122     private Class<?> superClass;
 123 
 124     EventInstrumentation(Class<?> superClass, byte[] bytes, long id) {
 125         this.superClass = superClass;
 126         this.classNode = createClassNode(bytes);
 127         this.settingInfos = buildSettingInfos(superClass, classNode);
 128         this.fieldInfos = buildFieldInfos(superClass, classNode);
 129         this.writeMethod = makeWriteMethod(fieldInfos);
 130         this.eventHandlerXInternalName = ASMToolkit.getInternalName(EventHandlerCreator.makeEventHandlerName(id));
 131         String n =  annotationValue(classNode, ANNOTATION_TYPE_NAME.getDescriptor(), String.class);
 132         this.eventName = n == null ? classNode.name.replace("/", ".") : n;
 133 
 134     }
 135 
 136     public String getClassName() {
 137       return classNode.name.replace("/",".");
 138     }
 139 
 140     private ClassNode createClassNode(byte[] bytes) {
 141         ClassNode classNode = new ClassNode();
 142         ClassReader classReader = new ClassReader(bytes);
 143         classReader.accept(classNode, 0);
 144         return classNode;
 145     }
 146 
 147     boolean isRegistered() {
 148         Boolean result = annotationValue(classNode, ANNOTATION_TYPE_REGISTERED.getDescriptor(), Boolean.class);
 149         if (result != null) {
 150             return result.booleanValue();
 151         }
 152         if (superClass != null) {
 153             Registered r = superClass.getAnnotation(Registered.class);
 154             if (r != null) {
 155                 return r.value();
 156             }
 157         }
 158         return true;
 159     }
 160 
 161     boolean isEnabled() {
 162         Boolean result = annotationValue(classNode, ANNOTATION_TYPE_ENABLED.getDescriptor(), Boolean.class);
 163         if (result != null) {
 164             return result.booleanValue();
 165         }
 166         if (superClass != null) {
 167             Enabled e = superClass.getAnnotation(Enabled.class);
 168             if (e != null) {
 169                 return e.value();
 170             }
 171         }
 172         return true;
 173     }
 174 
 175     @SuppressWarnings("unchecked")
 176     private static <T> T annotationValue(ClassNode classNode, String typeDescriptor, Class<?> type) {
 177         if (classNode.visibleAnnotations != null) {
 178             for (AnnotationNode a : classNode.visibleAnnotations) {
 179                 if (typeDescriptor.equals(a.desc)) {
 180                     List<Object> values = a.values;
 181                     if (values != null && values.size() == 2) {
 182                         Object key = values.get(0);
 183                         Object value = values.get(1);
 184                         if (key instanceof String && value != null) {
 185                             if (type == value.getClass()) {
 186                                 String keyName = (String) key;
 187                                 if ("value".equals(keyName)) {
 188                                    return (T) value;
 189                                 }
 190                             }
 191                         }
 192                     }
 193                 }
 194             }
 195         }
 196         return null;
 197     }
 198 
 199     private static List<SettingInfo> buildSettingInfos(Class<?> superClass, ClassNode classNode) {
 200         Set<String> methodSet = new HashSet<>();
 201         List<SettingInfo> settingInfos = new ArrayList<>();
 202         String settingDescriptor = Type.getType(SettingDefinition.class).getDescriptor();
 203         for (MethodNode m : classNode.methods) {
 204             if (m.visibleAnnotations != null) {
 205                 for (AnnotationNode an : m.visibleAnnotations) {
 206                     // We can't really validate the method at this
 207                     // stage. We would need to check that the parameter
 208                     // is an instance of SettingControl.
 209                     if (settingDescriptor.equals(an.desc)) {
 210                         Type returnType = Type.getReturnType(m.desc);
 211                         if (returnType.equals(Type.getType(Boolean.TYPE))) {
 212                             Type[] args = Type.getArgumentTypes(m.desc);
 213                             if (args.length == 1) {
 214                                 Type paramType = args[0];
 215                                 String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size();
 216                                 int index = settingInfos.size();
 217                                 SettingInfo si = new SettingInfo(fieldName, index);
 218                                 si.methodName = m.name;
 219                                 si.settingDescriptor = paramType.getDescriptor();
 220                                 si.internalSettingName = paramType.getInternalName();
 221                                 methodSet.add(m.name);
 222                                 settingInfos.add(si);
 223                             }
 224                         }
 225                     }
 226                 }
 227             }
 228         }
 229         for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) {
 230             for (java.lang.reflect.Method method : c.getDeclaredMethods()) {
 231                 if (!methodSet.contains(method.getName())) {
 232                     // skip private method in base classes
 233                     if (!Modifier.isPrivate(method.getModifiers())) {
 234                         if (method.getReturnType().equals(Boolean.TYPE)) {
 235                             if (method.getParameterCount() == 1) {
 236                                 Parameter param = method.getParameters()[0];
 237                                 Type paramType = Type.getType(param.getType());
 238                                 String fieldName = EventControl.FIELD_SETTING_PREFIX + settingInfos.size();
 239                                 int index = settingInfos.size();
 240                                 SettingInfo si = new SettingInfo(fieldName, index);
 241                                 si.methodName = method.getName();
 242                                 si.settingDescriptor = paramType.getDescriptor();
 243                                 si.internalSettingName = paramType.getInternalName();
 244                                 methodSet.add(method.getName());
 245                                 settingInfos.add(si);
 246                             }
 247                         }
 248                     }
 249                 }
 250             }
 251         }
 252         return settingInfos;
 253 
 254     }
 255 
 256     private static List<FieldInfo> buildFieldInfos(Class<?> superClass, ClassNode classNode) {
 257         Set<String> fieldSet = new HashSet<>();
 258         List<FieldInfo> fieldInfos = new ArrayList<>(classNode.fields.size());
 259         // These two field are added by native as transient so they will be
 260         // ignored by the loop below.
 261         // The benefit of adding them manually is that we can
 262         // control in which order they occur and we can add @Name, @Description
 263         // in Java, instead of in native. It also means code for adding implicit
 264         // fields for native can be reused by Java.
 265         fieldInfos.add(new FieldInfo("startTime", Type.LONG_TYPE.getDescriptor(), classNode.name));
 266         fieldInfos.add(new FieldInfo("duration", Type.LONG_TYPE.getDescriptor(), classNode.name));
 267         for (FieldNode field : classNode.fields) {
 268             String className = Type.getType(field.desc).getClassName();
 269             if (!fieldSet.contains(field.name) && isValidField(field.access, className)) {
 270                 FieldInfo fi = new FieldInfo(field.name, field.desc, classNode.name);
 271                 fieldInfos.add(fi);
 272                 fieldSet.add(field.name);
 273             }
 274         }
 275         for (Class<?> c = superClass; c != Event.class; c = c.getSuperclass()) {
 276             for (Field field : c.getDeclaredFields()) {
 277                 // skip private field in base classes
 278                 if (!Modifier.isPrivate(field.getModifiers())) {
 279                     if (isValidField(field.getModifiers(), field.getType().getName())) {
 280                         String fieldName = field.getName();
 281                         if (!fieldSet.contains(fieldName)) {
 282                             Type fieldType = Type.getType(field.getType());
 283                             String internalClassName = ASMToolkit.getInternalName(c.getName());
 284                             fieldInfos.add(new FieldInfo(fieldName, fieldType.getDescriptor(), internalClassName));
 285                             fieldSet.add(fieldName);
 286                         }
 287                     }
 288                 }
 289             }
 290         }
 291         return fieldInfos;
 292     }
 293 
 294     public static boolean isValidField(int access, String className) {
 295         if (Modifier.isTransient(access) || Modifier.isStatic(access)) {
 296             return false;
 297         }
 298         return jdk.jfr.internal.Type.isValidJavaFieldType(className);
 299     }
 300 
 301     public byte[] buildInstrumented() {
 302         makeInstrumented();
 303         return toByteArray();
 304     }
 305 
 306     private byte[] toByteArray() {
 307         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
 308         classNode.accept(cw);
 309         cw.visitEnd();
 310         byte[] result = cw.toByteArray();
 311         Utils.writeGeneratedASM(classNode.name, result);
 312         return result;
 313     }
 314 
 315     public byte[] builUninstrumented() {
 316         makeUninstrumented();
 317         return toByteArray();
 318     }
 319 
 320     private void makeInstrumented() {
 321         // MyEvent#isEnabled()
 322         updateMethod(METHOD_IS_ENABLED, methodVisitor -> {
 323             Label nullLabel = new Label();
 324             if (guardHandlerReference) {
 325                 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
 326                 methodVisitor.visitJumpInsn(Opcodes.IFNULL, nullLabel);
 327             }
 328             methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, TYPE_EVENT_HANDLER.getDescriptor());
 329             ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_IS_ENABLED);
 330             methodVisitor.visitInsn(Opcodes.IRETURN);
 331             if (guardHandlerReference) {
 332                 methodVisitor.visitLabel(nullLabel);
 333                 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
 334                 methodVisitor.visitInsn(Opcodes.ICONST_0);
 335                 methodVisitor.visitInsn(Opcodes.IRETURN);
 336             }
 337         });
 338 
 339         // MyEvent#begin()
 340         updateMethod(METHOD_BEGIN, methodVisitor -> {
 341             methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
 342             ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP);
 343             methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
 344             methodVisitor.visitInsn(Opcodes.RETURN);
 345         });
 346 
 347         // MyEvent#end()
 348         updateMethod(METHOD_END, methodVisitor -> {
 349             methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
 350             methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
 351             methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
 352             ASMToolkit.invokeStatic(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_DURATION);
 353             methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
 354             methodVisitor.visitInsn(Opcodes.RETURN);
 355             methodVisitor.visitMaxs(0, 0);
 356         });
 357 
 358         // MyEvent#commit() - Java event writer
 359         updateMethod(METHOD_COMMIT, methodVisitor -> {
 360                 // if (!isEnable()) {
 361                 // return;
 362                 // }
 363                 methodVisitor.visitCode();
 364                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 365                 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_IS_ENABLED.getName(), METHOD_IS_ENABLED.getDescriptor(), false);
 366                 Label l0 = new Label();
 367                 methodVisitor.visitJumpInsn(Opcodes.IFNE, l0);
 368                 methodVisitor.visitInsn(Opcodes.RETURN);
 369                 methodVisitor.visitLabel(l0);
 370                 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
 371                 // if (startTime == 0) {
 372                 // startTime = EventWriter.timestamp();
 373                 // } else {
 374                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 375                 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
 376                 methodVisitor.visitInsn(Opcodes.LCONST_0);
 377                 methodVisitor.visitInsn(Opcodes.LCMP);
 378                 Label durationalEvent = new Label();
 379                 methodVisitor.visitJumpInsn(Opcodes.IFNE, durationalEvent);
 380                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 381                 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(),
 382                         METHOD_TIME_STAMP.getDescriptor(), false);
 383                 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_START_TIME, "J");
 384                 Label commit = new Label();
 385                 methodVisitor.visitJumpInsn(Opcodes.GOTO, commit);
 386                 // if (duration == 0) {
 387                 // duration = EventWriter.timestamp() - startTime;
 388                 // }
 389                 // }
 390                 methodVisitor.visitLabel(durationalEvent);
 391                 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
 392                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 393                 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
 394                 methodVisitor.visitInsn(Opcodes.LCONST_0);
 395                 methodVisitor.visitInsn(Opcodes.LCMP);
 396                 methodVisitor.visitJumpInsn(Opcodes.IFNE, commit);
 397                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 398                 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, TYPE_EVENT_HANDLER.getInternalName(), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
 399                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 400                 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_START_TIME, "J");
 401                 methodVisitor.visitInsn(Opcodes.LSUB);
 402                 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, getInternalClassName(), FIELD_DURATION, "J");
 403                 methodVisitor.visitLabel(commit);
 404                 // if (shouldCommit()) {
 405                 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
 406                 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 407                 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), METHOD_EVENT_SHOULD_COMMIT.getName(), METHOD_EVENT_SHOULD_COMMIT.getDescriptor(), false);
 408                 Label end = new Label();
 409                 // eventHandler.write(...);
 410                 // }
 411                 methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
 412                 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
 413 
 414                 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
 415                 for (FieldInfo fi : fieldInfos) {
 416                     methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 417                     methodVisitor.visitFieldInsn(Opcodes.GETFIELD, fi.internalClassName, fi.fieldName, fi.fieldDescriptor);
 418                 }
 419 
 420                 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventHandlerXInternalName, writeMethod.getName(), writeMethod.getDescriptor(), false);
 421                 methodVisitor.visitLabel(end);
 422                 methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
 423                 methodVisitor.visitInsn(Opcodes.RETURN);
 424                 methodVisitor.visitEnd();
 425             });
 426 
 427         // MyEvent#shouldCommit()
 428         updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
 429             Label fail = new Label();
 430             // if (!eventHandler.shoouldCommit(duration) goto fail;
 431             methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
 432             methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
 433             methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
 434             ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT);
 435             methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail);
 436             for (SettingInfo si : settingInfos) {
 437                 // if (!settingsMethod(eventHandler.settingX)) goto fail;
 438                 methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
 439                 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
 440                 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
 441                 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
 442                 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName);
 443                 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, getInternalClassName(), si.methodName, "(" + si.settingDescriptor + ")Z", false);
 444                 methodVisitor.visitJumpInsn(Opcodes.IFEQ, fail);
 445             }
 446             // return true
 447             methodVisitor.visitInsn(Opcodes.ICONST_1);
 448             methodVisitor.visitInsn(Opcodes.IRETURN);
 449             // return false
 450             methodVisitor.visitLabel(fail);
 451             methodVisitor.visitInsn(Opcodes.ICONST_0);
 452             methodVisitor.visitInsn(Opcodes.IRETURN);
 453         });
 454     }
 455 
 456     private void makeUninstrumented() {
 457         updateExistingWithReturnFalse(METHOD_EVENT_SHOULD_COMMIT);
 458         updateExistingWithReturnFalse(METHOD_IS_ENABLED);
 459         updateExistingWithEmptyVoidMethod(METHOD_COMMIT);
 460         updateExistingWithEmptyVoidMethod(METHOD_BEGIN);
 461         updateExistingWithEmptyVoidMethod(METHOD_END);
 462     }
 463 
 464     private final void updateExistingWithEmptyVoidMethod(Method voidMethod) {
 465         updateMethod(voidMethod, methodVisitor -> {
 466             methodVisitor.visitInsn(Opcodes.RETURN);
 467         });
 468     }
 469 
 470     private final void updateExistingWithReturnFalse(Method voidMethod) {
 471         updateMethod(voidMethod, methodVisitor -> {
 472             methodVisitor.visitInsn(Opcodes.ICONST_0);
 473             methodVisitor.visitInsn(Opcodes.IRETURN);
 474         });
 475     }
 476 
 477     private MethodNode getMethodNode(Method method) {
 478         for (MethodNode m : classNode.methods) {
 479             if (m.name.equals(method.getName()) && m.desc.equals(method.getDescriptor())) {
 480                 return m;
 481             }
 482         }
 483         return null;
 484     }
 485 
 486     private final void updateMethod(Method method, Consumer<MethodVisitor> code) {
 487         MethodNode old = getMethodNode(method);
 488         int index = classNode.methods.indexOf(old);
 489         classNode.methods.remove(old);
 490         MethodVisitor mv = classNode.visitMethod(old.access, old.name, old.desc, null, null);
 491         mv.visitCode();
 492         code.accept(mv);
 493         mv.visitMaxs(0, 0);
 494         MethodNode newMethod = getMethodNode(method);
 495         classNode.methods.remove(newMethod);
 496         classNode.methods.add(index, newMethod);
 497     }
 498 
 499     public static Method makeWriteMethod(List<FieldInfo> fields) {
 500         StringBuilder sb = new StringBuilder();
 501         sb.append("(");
 502         for (FieldInfo v : fields) {
 503             sb.append(v.fieldDescriptor);
 504         }
 505         sb.append(")V");
 506         return new Method("write", sb.toString());
 507     }
 508 
 509     private String getInternalClassName() {
 510         return classNode.name;
 511     }
 512 
 513     public List<SettingInfo> getSettingInfos() {
 514         return settingInfos;
 515     }
 516 
 517     public List<FieldInfo> getFieldInfos() {
 518         return fieldInfos;
 519     }
 520 
 521     public String getEventName() {
 522         return eventName;
 523     }
 524 
 525     public void setGuardHandler(boolean guardHandlerReference) {
 526         this.guardHandlerReference = guardHandlerReference;
 527     }
 528 }