title: Is there a Java decompiler (be it standalone or an Eclipse plugin) that is able to decompile code woven by AspectJ?
body: <p>I have scanned the various related questions on the Internet (like <a href="http://www.java-decompiler.com/" rel="nofollow noreferrer">http://www.java-decompiler.com/</a>) and <a href="https://stackoverflow.com/search?q=java%20decompiler">SO</a> in particular. So far, I could only find two Java decompilers - <a href="http://java.decompiler.free.fr/?q=jdgui" rel="nofollow noreferrer">JD-GUI</a> and <a href="http://members.fortunecity.com/neshkov/dj.html" rel="nofollow noreferrer">DJ Java Decompiler</a> that claim to be up-to-date.</p>
  <p>All the rest are either unavailable for download or discontinued.</p>
  <p>So, I took a .class file containing AspectJ weaven code and decompiled it using both of the available decompilers. Observe the results:</p>
  <ol>
  <li>JD-GUI:</li>
  </ol>
  <p><img src="https://i.stack.imgur.com/miHBe.png" alt="enter image description here" /></p>
  <ol start="2">
  <li>DJ Java Decompiler:</li>
  </ol>
  <p><img src="https://i.stack.imgur.com/U7nSZ.png" alt="enter image description here" /></p>
  <p>As you can see both tools fail to decompile the Java code woven with AspectJ.</p>
  <p>Now I am not being too picky, I am just used to the .NET Reflector and looking for the same quality in a Java decompiler, be it stand-alone or Eclipse plugin, free or commercial.</p>
  <p>I am looking for one that actually works, and is convenient to use.</p>
  <h2>Edit</h2>
  <p>The general tenor of the responses to my question is something like this - &quot;Well, what do you want? Although, AspectJ creates valid JVM bytecode, this bytecode cannot be translated into valid Java&quot;. All I can say is that I do not subscribe to this point of view.</p>
  <p>Let me present you some more context and I hope you will agree that the tools can and should do better.</p>
  <p>The decompiled Java class was weaven using the following aspect:</p>
  <pre><code>public abstract aspect LoggingAspect {
    declare parents: (@LogMe *) implements ILoggable;
    
    public Logger ILoggable.getLogger() {
      LoggerHolderAspect holder = LoggerHolderAspect.aspectOf(this.getClass());
      return holder.getLogger();
    }
  
    abstract pointcut loggedMethods();
    
    before(ILoggable o): loggedMethods() &amp;&amp; this(o) {
      logBefore(o.getLogger(), thisJoinPoint);
    }
  
    after(ILoggable o) returning (Object result): loggedMethods() &amp;&amp; this(o) {
      logAfterReturning(o.getLogger(), thisJoinPoint, result);
    }
    
    after(ILoggable o) throwing (Exception e): loggedMethods() &amp;&amp; this(o) {
      logAfterThrowing(o.getLogger(), thisJoinPoint, e);
    }
  
    protected void logBefore(Logger l, JoinPoint jp) { ... }
    protected void logAfterReturning(Logger l, JoinPoint jp, Object result) { ... }
    protected void logAfterThrowing(Logger l, JoinPoint jp, Exception e) { ... }
  }
  </code></pre>
  <p>Now, the class being weaven is this:</p>
  <pre><code>@Path(&quot;user&quot;)
  public class UserHandler {
    ...    
    
    @GET
    @Path(&quot;{id}&quot;)
    @Produces({ &quot;application/json&quot;, &quot;application/xml&quot; })
    public User getUser(@PathParam(&quot;id&quot;) int id) { ... }
  
    @DELETE
    @Path(&quot;{id}&quot;)
    public void deleteUser(@PathParam(&quot;id&quot;) int id) { ... }
  
    @PUT
    @Path(&quot;{id}&quot;)
    public void putUser(@PathParam(&quot;id&quot;) int id, User entity) { ... }
  
    @POST
    @Produces({ &quot;application/json&quot;, &quot;application/xml&quot; })
    public Response postUser(User entity) { ... }
  }
  </code></pre>
  <p>Now, the JD-GUI simply fails to decompile each and every instrumented method correctly. The produced output looks like a badly done SVN merge. Here is the link to the complete file for the curious - <a href="http://pastebin.com/raw.php?i=WEmMNCPS" rel="nofollow noreferrer">http://pastebin.com/raw.php?i=WEmMNCPS</a></p>
  <p>The output produced by DJ Java Decompiler is slightly better. Seems like DJ has a problem with non void methods. Indeed, observe how it decompiles a void method:</p>
  <pre><code>@DELETE
  @Path(value=&quot;{id}&quot;)
  public void deleteUser(@PathParam(value=&quot;id&quot;) int id)
  {
      int i = id;
      org.aspectj.lang.JoinPoint joinpoint = Factory.makeJP(ajc$tjp_1, this, this, Conversions.intObject(i));
      try
      {
          ResourceHandlerLoggingAspect.aspectOf().ajc$before$com_shunra_poc_logging_LoggingAspect$1$51e061ae(this, joinpoint);
          if(!securityContext.isUserInRole(Enroler.ADMINISTRATOR.getName()))
          {
              throw new WebApplicationException(javax.ws.rs.core.Response.Status.UNAUTHORIZED);
          } else
          {
              m_userService.delete(id);
              Object obj = null;
              ResourceHandlerLoggingAspect.aspectOf().ajc$afterReturning$com_shunra_poc_logging_LoggingAspect$2$51e061ae(this, obj, joinpoint);
              return;
          }
      }
      catch(Exception exception)
      {
          ResourceHandlerLoggingAspect.aspectOf().ajc$afterThrowing$com_shunra_poc_logging_LoggingAspect$3$51e061ae(this, exception, joinpoint);
          throw exception;
      }
  }
  </code></pre>
  <p>This is fine, but if a method returns something, then DJ fails, for instance:</p>
  <pre><code>@POST
  @Produces(value={&quot;application/json&quot;, &quot;application/xml&quot;})
  public Response postUser(User entity)
  {
      org.aspectj.lang.JoinPoint joinpoint;
      User user = entity;
      joinpoint = Factory.makeJP(ajc$tjp_3, this, this, user);
      ResourceHandlerLoggingAspect.aspectOf().ajc$before$com_shunra_poc_logging_LoggingAspect$1$51e061ae(this, joinpoint);
      entity.Id = 0;
      m_userService.post(entity);
      Response response;
      Response response1 = response = Response.created(postedUserLocation(entity.Id)).entity(new EntityPostResult(entity.Id)).build();
      ResourceHandlerLoggingAspect.aspectOf().ajc$afterReturning$com_shunra_poc_logging_LoggingAspect$2$51e061ae(this, response1, joinpoint);
      return response;
      Exception exception;
      exception;
      ResourceHandlerLoggingAspect.aspectOf().ajc$afterThrowing$com_shunra_poc_logging_LoggingAspect$3$51e061ae(this, exception, joinpoint);
      throw exception;
  }
  </code></pre>
  <p>Again, here is the complete output for the curious - <a href="http://pastebin.com/raw.php?i=Qnwjm16y" rel="nofollow noreferrer">http://pastebin.com/raw.php?i=Qnwjm16y</a></p>
  <p>What exactly is being done by AspectJ, that these decompilers cannot cope with? All I want is a Java decompiler that is on a par with the .NET Reflector, which has absolutely no problem decompiling instrumented C# code, except really special cases and we are not there.</p>
  <h2>Edit 2</h2>
  <p>Following the suggestion of <a href="https://stackoverflow.com/users/441008/andyclement">Andy Clement</a> I have created a small application just to test how AspectJ instruments the code, compare it against manual instrumentation and see how JD-GUI and DJ decompile it. Below are my findings:</p>
  <p>The Java source code is:</p>
  <pre><code>public class Program {
    private static boolean doThrow;
  
    public static void main(String[] args) {
      run();
      doThrow = true;
      run();
    }
  
    private static void run() {
      System.out.println(&quot;===============================================&quot;);
      System.out.println(&quot;doThrow = &quot; + doThrow);
      System.out.println(&quot;===============================================&quot;);
      System.out.println(&quot;autoInstrumented:&quot;);
      try {
        System.out.println(autoInstrumented());
      } catch (Exception e) {
        System.out.println(e.getMessage());
      }
      System.out.println(&quot;===============================================&quot;);
      System.out.println(&quot;manuallyInstrumented:&quot;);
      try {
        System.out.println(manuallyInstrumented());
      } catch (Exception e) {
        System.out.println(e.getMessage());
      }
      System.out.println(&quot;===============================================&quot;);
    }
  
    public static void before() {
      System.out.println(&quot;before(f)&quot;);
    }
  
    public static void afterReturning(int x) {
      System.out.println(&quot;afterReturning(f) = &quot; + x);
    }
  
    public static void afterThrowing(Exception e) {
      System.out.println(&quot;afterThrowing(f) = &quot; + e.getMessage());
    }
  
    public static int f() throws Exception {
      if (doThrow) {
        throw new Exception(&quot;*** EXCEPTION !!! ***&quot;);
      }
      return 10;
    }
  
    public static int autoInstrumented() throws Exception {
      return f();
    }
  
    public static int manuallyInstrumented() throws Exception {
      before();
      try {
        int result = f();
        afterReturning(result);
        return result;
      } catch (Exception e) {
        afterThrowing(e);
        throw e;
      }
    }
  }
  </code></pre>
  <p>The aspect code is:</p>
  <pre><code>public aspect Weave {
    pointcut autoInstrumented() : execution(int Program.autoInstrumented());
    
    before() : autoInstrumented() {
      Program.before();
    }
    
    after() returning (int result) : autoInstrumented() {
      Program.afterReturning(result);
    }
  
    after() throwing (Exception e) : autoInstrumented() {
      Program.afterThrowing(e);
    }
  }
  </code></pre>
  <p>The output produced by DJ is:</p>
  <pre><code>public static int autoInstrumented()
      throws Exception
  {
      Weave.aspectOf().ajc$before$Weave$1$be1609d6();
      int i;
      int j = i = f();
      Weave.aspectOf().ajc$afterReturning$Weave$2$be1609d6(j);
      return i;
      Exception exception;
      exception;
      Weave.aspectOf().ajc$afterThrowing$Weave$3$be1609d6(exception);
      throw exception;
  }
  </code></pre>
  <p>Putting aside the names of the AspectJ created methods, the produced Java code is both wrong and invalid. It is wrong, because there is no try-catch statement. It is invalid because <code>exception;</code> is not a valid Java statement.</p>
  <p>Next comes JD-GUI:</p>
  <pre><code>public static int autoInstrumented() throws Exception {
    try {
      Weave.aspectOf().ajc$before$Weave$1$be1609d6();
      int i;
      int j = i = f(); Weave.aspectOf().ajc$afterReturning$Weave$2$be1609d6(j); return i; } catch (Exception localException) { Weave.aspectOf().ajc$afterThrowing$Weave$3$be1609d6(localException); } throw localException;
  }
  </code></pre>
  <p>I have to take my words about the JD-GUI producing corrupted output back. It so happens that the code is more or less correct, <strong>but</strong> all of the method tail is output on a single line! When viewing inside the GUI it looks like the method is truncated. Only after copying the code, pasting in Eclipse and reformatting can one see, that it is almost OK:</p>
  <pre><code>public static int autoInstrumented() throws Exception {
    try {
      Weave.aspectOf().ajc$before$Weave$1$be1609d6();
      int i;
      int j = i = f();
      Weave.aspectOf().ajc$afterReturning$Weave$2$be1609d6(j);
      return i;
    } catch (Exception localException) {
      Weave.aspectOf().ajc$afterThrowing$Weave$3$be1609d6(localException);
    }
    throw localException;
  }
  </code></pre>
  <p>Almost, because how come the <code>throw localException;</code> statement finds itself outside the catch block?</p>
  <p>Now, as for the actual JVM byte code. I have used the ByteCode Outline Eclipse extension, here are the results:</p>
  <p>The manually instrumented method <code>manuallyInstrumented</code>:</p>
  <pre><code>public static manuallyInstrumented()I throws java/lang/Exception 
  ATTRIBUTE org.aspectj.weaver.MethodDeclarationLineNumber : unknown
    TRYCATCHBLOCK L0 L1 L2 java/lang/Exception
   L3
    INVOKESTATIC Program.before()V
   L0
    INVOKESTATIC Program.f()I
    ISTORE 0
   L4
    ILOAD 0
    INVOKESTATIC Program.afterReturning(I)V
   L5
    ILOAD 0
   L1
    IRETURN
   L2
   FRAME SAME1 java/lang/Exception
    ASTORE 0
   L6
    ALOAD 0
    INVOKESTATIC Program.afterThrowing(Ljava/lang/Exception;)V
   L7
    ALOAD 0
    ATHROW
   L8
    LOCALVARIABLE result I L4 L2 0
    LOCALVARIABLE e Ljava/lang/Exception; L6 L8 0
    MAXSTACK = 1
    MAXLOCALS = 1
  </code></pre>
  <p>The auto instrumented method <code>autoInstrumented</code>:</p>
  <pre><code>public static autoInstrumented()I throws java/lang/Exception 
  ATTRIBUTE org.aspectj.weaver.MethodDeclarationLineNumber : unknown
    TRYCATCHBLOCK L0 L1 L1 java/lang/Exception
   L0
    INVOKESTATIC Weave.aspectOf()LWeave;
    INVOKEVIRTUAL Weave.ajc$before$Weave$1$be1609d6()V
    INVOKESTATIC Program.f()I
    DUP
    ISTORE 0
    DUP
    ISTORE 1
    INVOKESTATIC Weave.aspectOf()LWeave;
    ILOAD 1
    INVOKEVIRTUAL Weave.ajc$afterReturning$Weave$2$be1609d6(I)V
    ILOAD 0
    IRETURN
   L1
    ASTORE 2
    INVOKESTATIC Weave.aspectOf()LWeave;
    ALOAD 2
    INVOKEVIRTUAL Weave.ajc$afterThrowing$Weave$3$be1609d6(Ljava/lang/Exception;)V
    ALOAD 2
    ATHROW
    MAXSTACK = 3
    MAXLOCALS = 3
  </code></pre>
  <p>I am not a JVM guru (mildly put), so I cannot tell whether the JVM byte code of the <code>autoInstrumented</code> is &quot;bad&quot;. Can you?</p>
  <p><strong>Summary</strong>:</p>
  <ul>
  <li>DJ is no good</li>
  <li>JD-GUI is almost there, but still produces bad Java and usability is awful - need to copy, paste and reformat in Eclipse to understand what is going on.</li>
  </ul>
  <p><strong>Bottomline</strong></p>
  <p>No love.</p>