/** * 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(); } }
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(); } }
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; }
/** * 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(); } } }
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(); }