예제 #1
0
파일: Rule.java 프로젝트: damc-dev/byteman
  /**
   * forward an execute request a rule identified by its unique key
   *
   * @param key a string key identifying the rule instance to be fired
   * @param recipient the recipient of the method from which execution of the rule was triggered or
   *     null if it was a static method
   * @param args the arguments of the method from which execution of the rule was triggered
   */
  public static void execute(String key, Object recipient, Object[] args) throws ExecuteException {
    boolean enabled = isTriggeringEnabled();
    if (!enabled) {
      // we don't trigger code while we are doing rule housekeeping
      return;
    }

    // disable triggering until we get into actual rule code

    disableTriggersInternal();

    try {
      Rule rule = ruleKeyMap.get(key);
      if (Transformer.isVerbose()) {
        System.out.println("Rule.execute called for " + key);
      }

      // if the key is no longer present it just means the rule has been decommissioned so return
      if (rule == null) {
        if (Transformer.isVerbose()) {
          System.out.println("Rule.execute for decommissioned key " + key);
        }
        return;
      }

      rule.execute(recipient, args);
    } finally {
      // restore the status quo -- we must have been enabled if we got to this method
      enableTriggers();
    }
  }
예제 #2
0
  public Object interpret(HelperAdapter helper) throws ExecuteException {
    Object recipientValue = null;
    try {
      if (recipient != null) {
        recipientValue = recipient.interpret(helper);
        if (recipientValue == null) {
          throw new ExecuteException(
              "MethodExpression.interpret : null recipient for method "
                  + token.getText()
                  + getPos());
        }
      }
      int argCount = arguments.size();

      Object[] argValues = new Object[argCount];
      for (int i = 0; i < argCount; i++) {
        argValues[i] = arguments.get(i).interpret(helper);
      }
      // execute setTriggering directly rather than via reflection
      // that way rule code switch off triggering for a rule injected
      // into code used by Method.invoke()
      if (method.equals(setTriggeringMethod)) {
        boolean setting = (Boolean) argValues[0];
        if (setting) {
          Rule.enableTriggers();
        } else {
          Rule.disableTriggers();
        }
        return true;
      }
      // we have to enable triggers whenever we call out to a method in case it contians a trigger
      // point
      // TODO - do we do this if the method is a built-in? i.e. if the target is an instance of the
      // helper class
      // TODO - this breaks the user disable option so fix it!
      Rule.enableTriggersInternal();
      return method.invoke(recipientValue, argValues);
    } catch (InvocationTargetException e) {
      Throwable th = e.getCause();
      if (th instanceof ExecuteException) {
        throw (ExecuteException) th;
      } else {
        throw new ExecuteException(
            "MethodExpression.interpret : exception invoking method " + token.getText() + getPos(),
            th);
      }
    } catch (ExecuteException e) {
      throw e;
    } catch (Exception e) {
      throw new ExecuteException(
          "MethodExpression.interpret : exception invoking method " + token.getText() + getPos(),
          e);
    } finally {
      // disable triggers again
      Rule.disableTriggersInternal();
    }
  }
예제 #3
0
  public static Class getHelperAdapter(Rule rule, Class helperClass, boolean compileToBytecode)
      throws CompileException {
    Class adapterClass;
    // ok we have to create the adapter class

    // n.b. we don't bother synchronizing here -- if another rule is racing to create an adapter
    // in parallel we don't really care about generating two of them -- we can use whichever
    // one gets installed last

    try {
      String helperName = Type.getInternalName(helperClass);
      String compiledHelperName;

      // we put the helper in the
      if (compileToBytecode) {
        compiledHelperName = helperName + "_HelperAdapter_Compiled_" + nextId();
      } else {
        compiledHelperName = helperName + "_HelperAdapter_Interpreted_" + nextId();
      }

      byte[] classBytes =
          compileBytes(rule, helperClass, helperName, compiledHelperName, compileToBytecode);
      String externalName = compiledHelperName.replace('/', '.');
      // dump the compiled class bytes if required
      Transformer.maybeDumpClass(externalName, classBytes);
      // ensure the class is loaded
      // think we need to load the generated helper using the class loader of the trigger class
      ClassLoader loader = rule.getLoader();
      adapterClass = loadHelperAdapter(loader, externalName, classBytes);
    } catch (CompileException ce) {
      throw ce;
    } catch (Throwable th) {
      if (compileToBytecode) {
        throw new CompileException(
            "Compiler.createHelperAdapter : exception creating compiled helper adapter for "
                + helperClass.getName(),
            th);
      } else {
        throw new CompileException(
            "Compiler.createHelperAdapter : exception creating interpreted helper adapter for "
                + helperClass.getName(),
            th);
      }
    }

    return adapterClass;
  }
예제 #4
0
  /**
   * The implementation of this method may transform the supplied class file and return a new
   * replacement class file.
   *
   * <p>Once a transformer has been registered with {@link
   * java.lang.instrument.Instrumentation#addTransformer Instrumentation.addTransformer}, the
   * transformer will be called for every new class definition and every class redefinition. The
   * request for a new class definition is made with {@link ClassLoader#defineClass
   * ClassLoader.defineClass}. The request for a class redefinition is made with {@link
   * java.lang.instrument.Instrumentation#redefineClasses Instrumentation.redefineClasses} or its
   * native equivalents. The transformer is called during the processing of the request, before the
   * class file bytes have been verified or applied.
   *
   * <p>If the implementing method determines that no transformations are needed, it should return
   * <code>null</code>. Otherwise, it should create a new <code>byte[]</code> array, copy the input
   * <code>classfileBuffer</code> into it, along with all desired transformations, and return the
   * new array. The input <code>classfileBuffer</code> must not be modified.
   *
   * <p>In the redefine case, the transformer must support the redefinition semantics. If a class
   * that the transformer changed during initial definition is later redefined, the transformer must
   * insure that the second class output class file is a legal redefinition of the first output
   * class file.
   *
   * <p>If the transformer believes the <code>classFileBuffer</code> does not represent a validly
   * formatted class file, it should throw an <code>IllegalClassFormatException</code>. Subsequent
   * transformers will still be called and the load or redefine will still be attempted. Throwing an
   * <code>IllegalClassFormatException</code> thus has the same effect as returning null but
   * facilitates the logging or debugging of format corruptions.
   *
   * @param originalLoader the defining loader of the class to be transformed, may be <code>null
   *     </code> if the bootstrap loader
   * @param className the name of the class in the internal form of fully qualified class and
   *     interface names as defined in <i>The Java Virtual Machine Specification</i>. For example,
   *     <code>"java/util/List"</code>.
   * @param classBeingRedefined if this is a redefine, the class being redefined, otherwise <code>
   *     null</code>
   * @param protectionDomain the protection domain of the class being defined or redefined
   * @param classfileBuffer the input byte buffer in class file format - must not be modified
   * @return a well-formed class file buffer (the result of the transform), or <code>null</code> if
   *     no transform is performed.
   * @throws java.lang.instrument.IllegalClassFormatException if the input does not represent a
   *     well-formed class file
   * @see java.lang.instrument.Instrumentation#redefineClasses
   */
  public byte[] transform(
      ClassLoader originalLoader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    boolean enabled = true;
    ClassLoader loader = originalLoader;
    try {
      enabled = Rule.disableTriggersInternal();

      byte[] newBuffer = classfileBuffer;
      // we only transform certain classes -- we do allow bootstrap classes whose loader is null
      // but we exclude byteman classes and java.lang classes
      String internalName = TypeHelper.internalizeClass(className);

      if (isBytemanClass(internalName) || !isTransformable(internalName)) {
        return null;
      }

      // we will need the super class name any outer class name and the name of the interfaces the
      // class implements

      ClassChecker checker = getClassChecker(newBuffer); // new ClassChecker(newBuffer);

      if (checker == null || checker.isInterface()) {
        return null;
      }

      /*
      if (checker.hasOuterClass()) {
          // we don't transform inner classes for now
          // TODO -- see if we can match and transform inner classes via the outer class
          return null;
      }
      */

      // TODO-- reconsider this as it is a bit dodgy as far as security is concerned

      if (loader == null) {
        loader = ClassLoader.getSystemClassLoader();
      }

      // if we need to traverse the interfaces then we have a DAG to deal with so
      // we had better find a way to avoid doing things twice

      LinkedList<String> toVisit = null;
      HashSet<String> visited = null;

      // ok, we need to check whether there are any class scripts associated with this class and if
      // so
      // we will consider transforming the byte code

      // TODO -- there are almost certainly concurrency issues to deal with here if rules are being
      // loaded/unloaded

      newBuffer = tryTransform(newBuffer, internalName, loader, internalName, false);

      int dotIdx = internalName.lastIndexOf('.');

      if (dotIdx > 0) {
        newBuffer =
            tryTransform(
                newBuffer, internalName, loader, internalName.substring(dotIdx + 1), false);
      }

      if (scriptRepository.checkInterfaces()) {
        // now we need to do the same for any interface scripts
        // n.b. resist the temptation to call classBeingRedefined.getInterfaces() as this will
        // cause the class to be resolved, losing any changes we install

        // we need to check the transitive closure of the binary links
        // Class implements Interface and Interface extends Interface for this class
        // which in general is a DAG.

        toVisit = new LinkedList<String>();
        visited = new HashSet<String>();

        // we start with the original list of implemented interfaces

        int interfaceCount = checker.getInterfaceCount();
        for (int i = 0; i < interfaceCount; i++) {
          String interfaceName = checker.getInterface(i);
          toVisit.add(interfaceName);
        }

        // ok now check each interface in turn while pushing its super interfaces
        // until we no longer have any new interfaces to check

        while (!toVisit.isEmpty()) {
          String interfaceName = toVisit.pop();
          String internalInterfaceName = TypeHelper.internalizeClass(interfaceName);
          if (!visited.contains(interfaceName)) {
            // avoid visiting  this interface again
            visited.add(interfaceName);
            // now see if we have any rules for this interface
            newBuffer = tryTransform(newBuffer, internalName, loader, internalInterfaceName, true);
            dotIdx = internalInterfaceName.lastIndexOf('.');
            if (dotIdx >= 0) {
              newBuffer =
                  tryTransform(
                      newBuffer,
                      internalName,
                      loader,
                      internalInterfaceName.substring(dotIdx + 1),
                      true);
            }
            // check the extends list of this interface for new interfaces to consider
            ClassChecker newChecker = getClassChecker(interfaceName, originalLoader);
            if (newChecker != null) {
              interfaceCount = newChecker.getInterfaceCount();
              for (int i = 0; i < interfaceCount; i++) {
                interfaceName = newChecker.getInterface(i);
                toVisit.add(interfaceName);
              }
            }
          }
        }
      }

      // checking supers is expensive so we obey the switch which disables it

      if (!skipOverrideRules()) {
        // ok, now check the superclass for this class and so on

        String superName = checker.getSuper();

        while (superName != null) {
          // we need to check the super class structure
          // n.b. we use the original loader here because we don't want to search the system loader
          // when we have a class in the bootstrap loader
          checker = getClassChecker(superName, originalLoader);

          if (checker == null || checker.hasOuterClass()) {
            // we don't transform inner classes for now
            // TODO -- see if we can match and transform inner classes via the outer class
            break;
          }

          newBuffer = tryTransform(newBuffer, internalName, loader, superName, false, true);
          dotIdx = superName.lastIndexOf('.');
          if (dotIdx > 0) {
            newBuffer =
                tryTransform(
                    newBuffer, internalName, loader, superName.substring(dotIdx + 1), false, true);
          }

          if (scriptRepository.checkInterfaces()) {
            // we need to do another DAG visit but only for interfaces not already considered

            int interfaceCount = checker.getInterfaceCount();
            for (int i = 0; i < interfaceCount; i++) {
              String interfaceName = checker.getInterface(i);
              toVisit.add(interfaceName);
            }

            // ok now check each interface in turn while pushing its super interfaces
            // until we no longer have any new interfaces to check

            while (!toVisit.isEmpty()) {
              String interfaceName = toVisit.pop();
              String internalInterfaceName = TypeHelper.internalizeClass(interfaceName);
              if (!visited.contains(interfaceName)) {
                // avoid visiting  this interface again
                visited.add(interfaceName);
                // now see if we have any rules for this interface
                newBuffer =
                    tryTransform(
                        newBuffer, internalName, loader, internalInterfaceName, true, true);
                dotIdx = interfaceName.lastIndexOf('.');
                if (dotIdx >= 0) {
                  newBuffer =
                      tryTransform(
                          newBuffer,
                          internalName,
                          loader,
                          internalInterfaceName.substring(dotIdx + 1),
                          true,
                          true);
                }
                // check the extends list of this interface for new interfaces to consider
                ClassChecker newChecker = getClassChecker(interfaceName, originalLoader);
                if (newChecker != null) {
                  interfaceCount = newChecker.getInterfaceCount();
                  for (int i = 0; i < interfaceCount; i++) {
                    interfaceName = newChecker.getInterface(i);
                    toVisit.add(interfaceName);
                  }
                }
              }
            }
          }
          // move on to the next super
          superName = checker.getSuper();
        }
      }

      if (newBuffer != classfileBuffer) {
        // see if we need to dump the transformed bytecode for checking
        maybeDumpClass(internalName, newBuffer);
        newBuffer =
            maybeVerifyTransformedBytes(originalLoader, internalName, protectionDomain, newBuffer);
        return newBuffer;
      } else {
        return null;
      }
    } finally {
      if (enabled) {
        Rule.enableTriggersInternal();
      }
    }
  }
예제 #5
0
  private static byte[] compileBytes(
      Rule rule,
      Class helperClass,
      String helperName,
      String compiledHelperName,
      boolean compileToBytecode)
      throws Exception {
    ClassWriter cw = new ClassWriter(0);
    FieldVisitor fv;
    MethodVisitor mv;
    AnnotationVisitor av0;
    // create the class as a subclass of the rule helper class, appending Compiled to the front
    // of the class name and a unique number to the end of the class helperName
    // also ensure it implements the HelperAdapter interface
    //
    // public class foo.bar.Compiled_<helper>_<NNN> extends foo.bar.<helper> implements
    // HelperAdapter

    cw.visit(
        V1_5,
        ACC_PUBLIC + ACC_SUPER,
        compiledHelperName,
        null,
        helperName,
        new String[] {"org/jboss/byteman/rule/helper/HelperAdapter"});
    // we need to install the source file name
    {
      String fullFileName = rule.getFile();
      int idx = fullFileName.lastIndexOf(java.io.File.separatorChar);
      String basicFileName = (idx < 0 ? fullFileName : fullFileName.substring(idx + 1));
      String debug = "// compiled from: " + fullFileName + "\n// generated by Byteman\n";
      cw.visitSource(basicFileName, debug);
    }
    {
      // we need a Hashmap field to hold the bindings
      //
      // private HashMap<String, Object> bindingMap;

      fv =
          cw.visitField(
              ACC_PRIVATE,
              "bindingMap",
              "Ljava/util/HashMap;",
              "Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Object;>;",
              null);
      fv.visitEnd();
    }
    {
      // and a rule field to hold the rule
      //
      // private Rule rule;

      fv =
          cw.visitField(
              ACC_PRIVATE,
              "rule",
              "Lorg/jboss/byteman/rule/Rule;",
              "Lorg/jboss/byteman/rule/Rule;",
              null);
      fv.visitEnd();
    }
    {
      // we need a constructor which takes a Rule as argument
      // if the helper implements a constructor which takes a Rule as argument then we invoke it
      // otherwise we invoke the empty helper constructor

      Constructor superConstructor = null;
      try {
        superConstructor = helperClass.getDeclaredConstructor(Rule.class);
      } catch (NoSuchMethodException e) {
        // hmm, ok see if there is an empty constructor
      } catch (SecurityException e) {
        throw new CompileException(
            "Compiler.compileBytes : unable to access constructor for helper class "
                + helperClass.getCanonicalName());
      }
      boolean superWantsRule = (superConstructor != null);
      if (!superWantsRule) {
        try {
          superConstructor = helperClass.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
          throw new CompileException(
              "Compiler.compileBytes : no valid constructor found for helper class "
                  + helperClass.getCanonicalName());
        } catch (SecurityException e) {
          throw new CompileException(
              "Compiler.compileBytes : unable to access constructor for helper class "
                  + helperClass.getCanonicalName());
        }
      }
      //
      //  public Compiled<helper>_<NNN>()Rule rule)
      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Lorg/jboss/byteman/rule/Rule;)V", null, null);
      mv.visitCode();
      // super();
      //
      // or
      //
      // super(Rule);
      if (superWantsRule) {
        mv.visitVarInsn(ALOAD, 0);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKESPECIAL, helperName, "<init>", "(Lorg/jboss/byteman/rule/Rule;)V");
      } else {
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, helperName, "<init>", "()V");
      }
      // bindingMap = new HashMap<String, Object);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitTypeInsn(NEW, "java/util/HashMap");
      mv.visitInsn(DUP);
      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
      mv.visitFieldInsn(PUTFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
      // this.rule = rule
      mv.visitVarInsn(ALOAD, 0);
      mv.visitVarInsn(ALOAD, 1);
      mv.visitFieldInsn(PUTFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
      // return;
      mv.visitInsn(RETURN);
      mv.visitMaxs(3, 2);
      mv.visitEnd();
    }
    {
      // create the execute method
      //
      // public void execute(Bindings bindings, Object recipient, Object[] args) throws
      // ExecuteException
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "execute",
              "(Ljava/lang/Object;[Ljava/lang/Object;)V",
              null,
              new String[] {"org/jboss/byteman/rule/exception/ExecuteException"});
      mv.visitCode();
      // if (Transformer.isVerbose())
      mv.visitMethodInsn(INVOKESTATIC, "org/jboss/byteman/agent/Transformer", "isVerbose", "()Z");
      Label l0 = new Label();
      mv.visitJumpInsn(IFEQ, l0);
      // then
      // System.out.println(rule.getName() + " execute");
      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
      mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
      mv.visitInsn(DUP);
      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
      mv.visitMethodInsn(
          INVOKEVIRTUAL, "org/jboss/byteman/rule/Rule", "getName", "()Ljava/lang/String;");
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          "java/lang/StringBuilder",
          "append",
          "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
      mv.visitLdcInsn(" execute()");
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          "java/lang/StringBuilder",
          "append",
          "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
      mv.visitMethodInsn(
          INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
      // end if
      mv.visitLabel(l0);

      Bindings bindings = rule.getBindings();
      Iterator<Binding> iterator = bindings.iterator();

      while (iterator.hasNext()) {
        Binding binding = iterator.next();
        String name = binding.getName();
        if (binding.isAlias()) {
          // lookups and updates will use the aliased name
          continue;
        }
        if (binding.isHelper()) {
          // bindingMap.put(name, this);
          mv.visitVarInsn(ALOAD, 0);
          mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
          mv.visitLdcInsn(name);
          mv.visitVarInsn(ALOAD, 0);
          mv.visitMethodInsn(
              INVOKEVIRTUAL,
              "java/util/HashMap",
              "put",
              "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
          mv.visitInsn(POP);
        } else if (binding.isRecipient()) {
          // bindingMap.put(name, recipient);
          mv.visitVarInsn(ALOAD, 0);
          mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
          mv.visitLdcInsn(name);
          mv.visitVarInsn(ALOAD, 1);
          mv.visitMethodInsn(
              INVOKEVIRTUAL,
              "java/util/HashMap",
              "put",
              "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
          mv.visitInsn(POP);
          // } else if (binding.isParam() || binding.isLocalVar() || binding.isReturn() ||
          //             binding.isThrowable() || binding.isParamCount() || binding.isParamArray())
          // {
        } else if (!binding.isBindVar()) {
          // bindingMap.put(name, args[binding.getCallArrayIndex()]);
          mv.visitVarInsn(ALOAD, 0);
          mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
          mv.visitLdcInsn(name);
          mv.visitVarInsn(ALOAD, 2);
          mv.visitLdcInsn(binding.getCallArrayIndex());
          mv.visitInsn(AALOAD);
          mv.visitMethodInsn(
              INVOKEVIRTUAL,
              "java/util/HashMap",
              "put",
              "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
          mv.visitInsn(POP);
        }
      }

      // execute0()
      mv.visitVarInsn(ALOAD, 0);
      mv.visitMethodInsn(INVOKEVIRTUAL, compiledHelperName, "execute0", "()V");

      // now restore update bindings

      iterator = bindings.iterator();

      while (iterator.hasNext()) {
        Binding binding = iterator.next();
        if (binding.isAlias()) {
          continue;
        }
        String name = binding.getName();

        if (binding.isUpdated()) {
          // if (binding.isParam() || binding.isLocalVar() || binding.isReturn()) {
          if (!binding.isBindVar()) {
            int idx = binding.getCallArrayIndex();
            // Object value = bindingMap.get(name);
            // args[idx] = value;
            mv.visitVarInsn(ALOAD, 2); // args
            mv.visitLdcInsn(idx);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
            mv.visitLdcInsn(name);
            mv.visitMethodInsn(
                INVOKEVIRTUAL,
                "java/util/HashMap",
                "get",
                "(Ljava/lang/Object;)Ljava/lang/Object;");
            mv.visitInsn(AASTORE);
          }
        }
      }

      // return
      mv.visitInsn(RETURN);
      mv.visitMaxs(4, 3);
      mv.visitEnd();
    }
    {
      // create the setBinding method
      //
      // public void setBinding(String name, Object value)
      mv =
          cw.visitMethod(
              ACC_PUBLIC, "setBinding", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null);
      mv.visitCode();
      //  bindingMap.put(name, value);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          "java/util/HashMap",
          "put",
          "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
      mv.visitInsn(POP);
      // return
      mv.visitInsn(RETURN);
      mv.visitMaxs(3, 3);
      mv.visitEnd();
    }
    {
      // create the getBinding method
      //
      // public Object getBinding(String name)
      mv =
          cw.visitMethod(
              ACC_PUBLIC, "getBinding", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);
      mv.visitCode();
      // {TOS} <== bindingMap.get(name);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "bindingMap", "Ljava/util/HashMap;");
      mv.visitVarInsn(ALOAD, 1);
      mv.visitMethodInsn(
          INVOKEVIRTUAL, "java/util/HashMap", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
      // return {TOS}
      mv.visitInsn(ARETURN);
      mv.visitMaxs(2, 2);
      mv.visitEnd();
    }
    {
      // create the getName method
      //
      // public String getName()
      mv = cw.visitMethod(ACC_PUBLIC, "getName", "()Ljava/lang/String;", null, null);
      mv.visitCode();
      // {TOS} <== rule.getName()
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
      mv.visitMethodInsn(
          INVOKEVIRTUAL, "org/jboss/byteman/rule/Rule", "getName", "()Ljava/lang/String;");
      // return {TOS}
      mv.visitInsn(ARETURN);
      mv.visitMaxs(1, 1);
      mv.visitEnd();
    }
    // create the getAccessibleField method
    //
    // public Object getAccessibleField(Object owner, int fieldIndex)
    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "getAccessibleField",
              "(Ljava/lang/Object;I)Ljava/lang/Object;",
              null,
              null);
      mv.visitCode();
      // {TOS} <== rule.getAccessibleField(owner, fieldIndex);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ILOAD, 2);
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          "org/jboss/byteman/rule/Rule",
          "getAccessibleField",
          "(Ljava/lang/Object;I)Ljava/lang/Object;");
      // return {TOS}
      mv.visitInsn(ARETURN);
      mv.visitMaxs(3, 3);
      mv.visitEnd();
    }

    // create the setAccessibleField method
    //
    // public void setAccessibleField(Object owner, Object value, int fieldIndex)
    // rule.setAccessibleField(owner, value, fieldIndex);
    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "setAccessibleField",
              "(Ljava/lang/Object;Ljava/lang/Object;I)V",
              null,
              null);
      mv.visitCode();
      // rule.setAccessibleField(owner, value, fieldIndex);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitVarInsn(ILOAD, 3);
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          "org/jboss/byteman/rule/Rule",
          "setAccessibleField",
          "(Ljava/lang/Object;Ljava/lang/Object;I)V");
      // return
      mv.visitInsn(RETURN);
      mv.visitMaxs(4, 4);
      mv.visitEnd();
    }

    // create the invokeAccessibleMethod method
    //
    // public Object invokeAccessibleMethod(Object target, Object[] args, int methodIndex)
    // {TOS} <==  rule.invokeAccessibleMethod(target, args, methodIndex);
    {
      mv =
          cw.visitMethod(
              ACC_PUBLIC,
              "invokeAccessibleMethod",
              "(Ljava/lang/Object;[Ljava/lang/Object;I)Ljava/lang/Object;",
              null,
              null);
      mv.visitCode();
      // rule.invokeAccessibleMethod(target, args, fieldIndex);
      mv.visitVarInsn(ALOAD, 0);
      mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
      mv.visitVarInsn(ALOAD, 1);
      mv.visitVarInsn(ALOAD, 2);
      mv.visitVarInsn(ILOAD, 3);
      mv.visitMethodInsn(
          INVOKEVIRTUAL,
          "org/jboss/byteman/rule/Rule",
          "invokeAccessibleMethod",
          "(Ljava/lang/Object;[Ljava/lang/Object;I)Ljava/lang/Object;");
      // return {TOS}
      mv.visitInsn(ARETURN);
      mv.visitMaxs(4, 4);
      mv.visitEnd();
    }
    if (compileToBytecode) {
      // we generate a single execute0 method if we want to run compiled and get
      // the event, condiiton and action to insert the relevant bytecode to implement
      // bind(), test() and fire()

      {
        // create the execute0() method
        //
        // private void execute0()
        mv =
            cw.visitMethod(
                ACC_PRIVATE,
                "execute0",
                "()V",
                null,
                new String[] {"org/jboss/byteman/rule/exception/ExecuteException"});
        mv.visitCode();
        CompileContext compileContext = new CompileContext(mv);
        // make sure we set the first line number before generating any code
        compileContext.notifySourceLine(rule.getLine());
        compileContext.addLocalCount(3); // for this and 2 object args
        // bind();
        rule.getEvent().compile(mv, compileContext);
        // if (test())
        rule.getCondition().compile(mv, compileContext);
        Label l0 = new Label();
        mv.visitJumpInsn(IFEQ, l0);
        compileContext.addStackCount(-1);
        // then
        rule.getAction().compile(mv, compileContext);
        // fire();
        // end if
        mv.visitLabel(l0);
        // this will match the ENDRULE line
        compileContext.notifySourceEnd();
        // return
        mv.visitInsn(RETURN);
        // need to specify correct Maxs values
        mv.visitMaxs(compileContext.getStackMax(), compileContext.getLocalMax());
        mv.visitEnd();
      }
    } else {
      // we generate the following methods if we want to run interpreted
      {
        // create the execute0() method
        //
        // private void execute0()
        mv =
            cw.visitMethod(
                ACC_PRIVATE,
                "execute0",
                "()V",
                null,
                new String[] {"org/jboss/byteman/rule/exception/ExecuteException"});
        mv.visitCode();
        // bind();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, compiledHelperName, "bind", "()V");
        // if (test())
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, compiledHelperName, "test", "()Z");
        Label l0 = new Label();
        mv.visitJumpInsn(IFEQ, l0);
        // then
        // fire();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, compiledHelperName, "fire", "()V");
        // end if
        mv.visitLabel(l0);
        // return
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
      }
      {
        // create the bind method
        //
        // private void bind()
        mv =
            cw.visitMethod(
                ACC_PRIVATE,
                "bind",
                "()V",
                null,
                new String[] {"org/jboss/byteman/rule/exception/ExecuteException"});
        mv.visitCode();
        // rule.getEvent().interpret(this);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "org/jboss/byteman/rule/Rule",
            "getEvent",
            "()Lorg/jboss/byteman/rule/Event;");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "org/jboss/byteman/rule/Event",
            "interpret",
            "(Lorg/jboss/byteman/rule/helper/HelperAdapter;)Ljava/lang/Object;");
        mv.visitInsn(RETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();
      }
      {
        // create the test method
        //
        // private boolean test()
        mv =
            cw.visitMethod(
                ACC_PRIVATE,
                "test",
                "()Z",
                null,
                new String[] {"org/jboss/byteman/rule/exception/ExecuteException"});
        mv.visitCode();
        // {TOS} <== rule.getCondition().interpret(this);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "org/jboss/byteman/rule/Rule",
            "getCondition",
            "()Lorg/jboss/byteman/rule/Condition;");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "org/jboss/byteman/rule/Condition",
            "interpret",
            "(Lorg/jboss/byteman/rule/helper/HelperAdapter;)Ljava/lang/Object;");
        mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
        // unbox the returned Boolean
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
        // return {TOS}
        mv.visitInsn(IRETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();
      }
      {
        // create the fire method
        //
        // private void fire()
        mv =
            cw.visitMethod(
                ACC_PRIVATE,
                "fire",
                "()V",
                null,
                new String[] {"org/jboss/byteman/rule/exception/ExecuteException"});
        mv.visitCode();
        // rule.getAction().interpret(this);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, compiledHelperName, "rule", "Lorg/jboss/byteman/rule/Rule;");
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "org/jboss/byteman/rule/Rule",
            "getAction",
            "()Lorg/jboss/byteman/rule/Action;");
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(
            INVOKEVIRTUAL,
            "org/jboss/byteman/rule/Action",
            "interpret",
            "(Lorg/jboss/byteman/rule/helper/HelperAdapter;)Ljava/lang/Object;");
        // return
        mv.visitInsn(RETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();
      }
    }

    cw.visitEnd();

    return cw.toByteArray();
  }