2 Replies Latest reply on May 9, 2017 2:33 PM by rmichoud

    Throwing an exception in a try/catch block

    rmichoud

      I think I hit a limitation and I am trying to investigate if there is a way to work around it.

       

      Let's take an example:

       

      Example.java

      ...

      try {

           PrintWriter writer = new PrintWriter("test", "UTF-8");
           writer.println("hello");

      } catch (IOException e) {

           e.printStackTrace();

      }

      ....

       

      I want to inject an IOException at the first line of the try block.

       

      It looks like that the only throw support in byteman is at the method level. When I tried, the rule would not compile. I removed the checkThrownTypeIsValid call in typeCheck method. Compiles all right but then the exception bypass the try/catch and directly get thrown at the method. The method (correctly) doesn't throw IOException.

       

      I would need some help to see if this is achievable. If it is, I can offer to do it as well. I need it .

       

      Roger

        • 1. Re: Throwing an exception in a try/catch block
          adinn

          Hi Roger,

           

          That is indeed a deliberate limitation. I'll explain why before providing a rule which does precisely what you want.

           

          Let's start with your example. If Byteman simply injected "throw new IOException" into the try block just before the new operation  then that would not change the method contract for your original code. The exception would be caught. So, the method would not generate an unexpected exception (I am assuming there is no outer try catch).

           

          Now imagine instead that you asked for the exception to be injected into the catch block. If Byteman simply did what you asked then this would cause an IOException to be thrown from the method that the code belongs to. That would be a very bad idea as it changes the method's contract with its callers (of course, if the method declared IOException as a checked exception then it would be legitimate to inject a throw into the catch block). Luckily, the JVM's byecode verifier checks code after agents apply transforms and rejects any transform that would produce a breaking change.

           

          So, I could implement a throw rule by injecting a throw directly into the bytecode at the injection point. Byteman could check the try-catch plumbing at every requested each injection point to see whether an injected throw made sense. If a throw is valid because it will be caught or because it will escape but still match the method's exception signature the Byteman could happily inject the rule. If not it could  notify an error. However, I decided that this was not how I wanted throw to work.

           

          The problem with this model is that your injected code is hostage to the try-catch structure of the method. Consider the case where a method catches an InterruptedException and then rethrows it

           

          ...
          connection = establishConnection();
          try {
              pollConnection(connection)
          } catch (InterruptedException e) {
              resetConnection(connection)
              throw e
          }
          ....
          

           

          In this case if Byteman injected "throw new InterruptedException" at the call to poll it would not bypass the handling code that resets the connection. That might actually be critical to establishing your test scenario and there is no way to work round it.

           

          So, Byteman implements throw as a short-circuit of the method from the injection point. If you inject a THROW rule at the call (AT INVOKE pollConnection) then when (IF) it executes the throw control exits the method immediately at the top level, bypassing any intervening catch blocks. n.b. Byteman adopts the same model when you inject  a RETURN rule. This behaviour means that it is easier to write correct rules. If you want to THROW an exception (or RETURN, with or without a value) then you simply need to ensure that the exception type (or return type) conforms to the method's exception (or return) declaration. You don't need to be sure that the injection point will be inside a compatible try block.

           

          So, in that case how do you achieve the result you want, where an exception is generated inside the try block and caught by the catch block. The answer is to inject it into the called method that generates the exception. With your code you could use a rule that looked like this

           

          RULE throw IOException so myMethod can catch it
          CLASS PrintWriter
          METHOD <init>
          AT ENTRY
          IF callerEquals("MyClass.myMethod", true)
          DO THROW new IOException("Byteman foils the evil villains once again!");
          ENDRULE
          

           

          The rule is injected into the constructor for PrintWriter. So, when your code creates a new PrintWriter it throws an exception. Notice that the condition includes a test which executes built-in method callerEquals to ensure that the calling method name and class match the caller you are interested in (the second argument true indicates that the supplied name "MyClass.myMethod" specifies both the class name and the method name).

          • 2. Re: Throwing an exception in a try/catch block
            rmichoud

            Hi Andrew,

             

            This is awesome, exactly what I wanted. I thought about triggering the exception from the called functions (here the constructor) but I didn't know that you could do a filter on the caller and I wanted the exception to be injected only in that function. Great!

             

            Keep up the good work!

             

            Roger