Bug 70384 - Anonymous inner classes generated by Eclipse 3.0M8+ cannot be decompiled by BCEL library
Summary: Anonymous inner classes generated by Eclipse 3.0M8+ cannot be decompiled by B...
Status: RESOLVED WORKSFORME
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.0   Edit
Hardware: PC Windows 2000
: P3 minor (vote)
Target Milestone: 3.1 M1   Edit
Assignee: Olivier Thomann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-07-19 17:29 EDT by Jody McDonnell CLA
Modified: 2005-01-11 11:02 EST (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jody McDonnell CLA 2004-07-19 17:29:08 EDT
We have a situation where we need to decompile .class files related to our
project. We are currently doing this using the decompiler provided with the BCEL
library.

What we have found is that .class files related to anonymous inner classes which
have been generated by the compiler provided with Eclipse 3.0M8 or later cause
the BCEL decompiler to exit with a java.lang.ArrayIndexOutOfBoundsException.
Class files generated by the Sun javac compiler or by the compiler included with
Eclipse 3.0M7 can be decompiled without any issue.

We have observed this issue occurring on both the Windows 2000 and Solaris 2.8
platforms.

We are using the BCEL 5.1 (bcel-5.1.jar from http://jakarta.apache.org/bcel/)
library. We do not have Cheetah installed.

The following code is an example of a class which does not decompile gracefully
under the Eclipse 3.0 release build:


----------------------------------------------------------------------------
import javax.swing.JDialog;

public abstract class DialogOkayCancel extends JDialog {

    public DialogOkayCancel() {
        super();
        this.setDefaultCloseOperation(
                 javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        this.addWindowListener(new java.awt.event.WindowAdapter() {

            public void windowClosing(java.awt.event.WindowEvent e) {
                cancelClicked();
            }
        });
    }

    protected void cancelClicked() {
        System.out.println("The cancel button has been clicked.");
    }
}
----------------------------------------------------------------------------


We have a small test program that we use to decompile the .class files. For the
purposes of this test, I would copy the DialogOkayCancel$1.class file to my D:\
root directory and run the following test program out of Eclipse using Run ->
Java Application:


----------------------------------------------------------------------------
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.util.BCELifier;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.FileInputStream;

public class Test {
    public static void main(String[] args) throws Exception {
    	InputStream target=new FileInputStream("d:\\DialogOkayCancel$1.class");
        ClassParser cpTarget = new ClassParser(target, null);
        JavaClass jcTarget = cpTarget.parse();

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        BCELifier bcel = new BCELifier(jcTarget, bos);
        bcel.start();
        bos.close();
        System.out.println(bos.toString());
    }
}
----------------------------------------------------------------------------


When run on a .class file generated by the 3.0M8 or later compiler, the program
exits with the following error message:


----------------------------------------------------------------------------
java.lang.ArrayIndexOutOfBoundsException: 12
	at org.apache.bcel.util.BCELifier.printFlags(BCELifier.java:239)
	at org.apache.bcel.util.BCELifier.printFlags(BCELifier.java:226)
	at org.apache.bcel.util.BCELifier.visitField(BCELifier.java:185)
	at org.apache.bcel.classfile.Field.accept(Field.java:107)
	at org.apache.bcel.util.BCELifier.visitJavaClass(BCELifier.java:136)
	at org.apache.bcel.util.BCELifier.start(BCELifier.java:89)
	at Test.main(Test.java:18)
Exception in thread "main" 
----------------------------------------------------------------------------


The same program run on the .class file generated by Eclipse 3.0M7 for the same
sample code will result in the decompiled source code being displayed properly.

We're not sure if this is an issue with the Eclipse compiler per se, but we're
curious to know what changed between 3.0M7 and 3.0M8 to cause the BCEL
decompiler to have problems.
Comment 1 Olivier Thomann CLA 2004-07-19 20:53:50 EDT
I will investigate.
Comment 2 Olivier Thomann CLA 2004-07-20 15:48:37 EDT
I cannot reproduce it if cheetah is not installed. If I compile with cheetah,
using the -1.5 compliance, it fails with the stack trace you have. I will
investigate changes between 1.5 compliance mode and 1.4 compliance mode.
I think that the difference is that using 1.5 compliance mode the synthetic is
now part of the field modifiers and not an attribute anymore. So this could
explain why  BCEL cannot disassemble it anymore. So if you stay with compliance
1.4 or 1.3, it should work.
Comment 3 Olivier Thomann CLA 2004-07-20 16:05:53 EDT
If I patch BCEL code to add synthetic as part of the field flag, then I get this:
import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.*;
import org.apache.bcel.*;
import java.io.*;

public class DialogOkayCancel$1Creator implements Constants {
  private InstructionFactory _factory;
  private ConstantPoolGen    _cp;
  private ClassGen           _cg;

  public DialogOkayCancel$1Creator() {
    _cg = new ClassGen("DialogOkayCancel$1", "java.awt.event.WindowAdapter",
"DialogOkayCancel.java", ACC_FINAL | ACC_SUPER, new String[] {  });

    _cp = _cg.getConstantPool();
    _factory = new InstructionFactory(_cg, _cp);
  }

  public void create(OutputStream out) throws IOException {
    createFields();
    createMethod_0();
    createMethod_1();
    _cg.getJavaClass().dump(out);
  }

  private void createFields() {
    FieldGen field;

    field = new FieldGen(ACC_FINAL | ACC_SYNTHETIC, new
ObjectType("DialogOkayCancel"), "this$0", _cp);
    _cg.addField(field.getField());
  }

  private void createMethod_0() {
    InstructionList il = new InstructionList();
    MethodGen method = new MethodGen(0, Type.VOID, new Type[] { new
ObjectType("DialogOkayCancel") }, new String[] { "arg0" }, "<init>",
"DialogOkayCancel$1", il, _cp);

    InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
    il.append(_factory.createLoad(Type.OBJECT, 1));
    il.append(_factory.createFieldAccess("DialogOkayCancel$1", "this$0", new
ObjectType("DialogOkayCancel"), Constants.PUTFIELD));
    InstructionHandle ih_5 = il.append(_factory.createLoad(Type.OBJECT, 0));
    il.append(_factory.createInvoke("java.awt.event.WindowAdapter", "<init>",
Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
    InstructionHandle ih_9 = il.append(_factory.createReturn(Type.VOID));
    method.setMaxStack();
    method.setMaxLocals();
    _cg.addMethod(method.getMethod());
    il.dispose();
  }

  private void createMethod_1() {
    InstructionList il = new InstructionList();
    MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, new Type[] { new
ObjectType("java.awt.event.WindowEvent") }, new String[] { "arg0" },
"windowClosing", "DialogOkayCancel$1", il, _cp);

    InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
    il.append(_factory.createFieldAccess("DialogOkayCancel$1", "this$0", new
ObjectType("DialogOkayCancel"), Constants.GETFIELD));
    il.append(_factory.createInvoke("DialogOkayCancel", "cancelClicked",
Type.VOID, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
    InstructionHandle ih_7 = il.append(_factory.createReturn(Type.VOID));
    method.setMaxStack();
    method.setMaxLocals();
    _cg.addMethod(method.getMethod());
    il.dispose();
  }

  public static void main(String[] args) throws Exception {
    DialogOkayCancel$1Creator creator = new DialogOkayCancel$1Creator();
    creator.create(new FileOutputStream("DialogOkayCancel$1.class"));
  }
}

So this was the issue you got.
Closing as WORKSFORME.