---+ ClassCracker 3 Java Decompiler Simple Tests I performed some simple tests on ClassCracker 3 (version 3.01), purely as a decompiler. %TOC% ---++ Fibo For source code, see DecompilerFiboTestSource. The output from ClassCracker was this:
/* CONVERSION by ClassCracker 3 */

import java.io.PrintStream;

class Fibo extends Object
{

  Fibo( )
  {
  }

  private static int fib( int locVar_0)
  {
	 if( locVar_0 > 1 )  {
		return  ( fib( ( locVar_0 - 1 ) ) + fib( ( locVar_0 - 2 ) ) );
	 }
	 return  locVar_0;
  }

  public static void main( String[] args)  throws Exception
  {
	 int locVar_1;
	 int locVar_2;
	 Object locVar_3;

	 locVar_1 = 0;
	 try {
		locVar_1 = Integer.parseInt( args[ 0 ] );
	 }
	 catch( Exception locVar_3 ) {
		System.out.println( "Input error" );
		System.exit( 1 );
	 }
	 locVar_2 = fib( locVar_1 );
	 System.out.println( (new StringBuffer(  )).toString(  ) + "fibonacci(" +
String.valueOf( locVar_1 ) + ") = " + String.valueOf( locVar_2 ) );
  }
}
The variable names aren't as easy to read as some of the other decompilers, but that's a minor point. The =println= call is also more difficult to read than most. When recompiled, the above results in an error:
Fibo.java:31: locVar_3 is already defined in main(java.lang.String[])
	 catch( Exception locVar_3 ) {
ClassCracker doesn't seem to have been tested with many programs containing exceptions. When I commented out the first declaration of locVar_3, the program compiled and ran correctly. ---++ Casting For source code, see DecompilerCastingTestSource. Here is the output from ClassCracker for =main=:
  public static void main( String[] args)
  {
	 int locVar_1;

	 locVar_1 = 0;
	 while( locVar_1 < 128 )  {
		System.out.println( (new StringBuffer(  )).toString(  ) + "ascii " +
String.valueOf( locVar_1 ) + " character " + String.valueOf( locVar_1 ) );
		locVar_1 = ( (char) ( locVar_1 + 1 ) );
	 }
  }
Here, the =char= loop variable has been changed to =int=, and there is no cast back to char, so the program does not work like the original. The output compiled without errors, and if a cast is added to the last use of =locVar_1=, the decompiled program works the same as the original. ---++ Inner classes For source code, see DecompilerInnerClassesTestSource. When decompiled with ClassCracker, the result is
/* CONVERSION by ClassCracker 3 */

public class Usa extends Object
{
  public String name;

  public Usa( )
  {
	 this.name = "Detroit";
  }
}
This is equivalent to what is literally in the file =Usa.class=. However, there is are also files =Usa$England.class= and =Usa$England$Ireland.class= (which show up clearly in ClassCracker's GUI interface). When the latter is decompiled, the result is
/* CONVERSION by ClassCracker 3 */

import java.io.PrintStream;

/*
NOTE:
This is an inner class named "Ireland"
  within a class named "England"
	 within a class named "Usa"
*/

public class Usa$England$Ireland extends Object
{
  public String name;
  private final Usa$England this$1;

  public Usa$England$Ireland( Usa$England locVar_1)
  {
	 this.this$1 = locVar_1;
	 this.name = "Dublin";
  }

  public void print_names( )
  {
	 System.out.println( this.name );
  }
}
Unfortunately, the above doesn't compile. At least, it appears to be "inner class aware". ---++ Deuces Wild As a much larger test (class file is about 30 times as large as Fibo's), I tried to decompile a file called deuceswild.class. It is an applet of some 38K bytes (.class file), which plays the casino game of Deuces Wild. It also calculates the optimal play, and prompts the user if an error (suboptimal play) is made. The decompilation was quite fast (for a decompiler written in Java); about 2 seconds, but only the first 5 methods are decompiled. Because of this, the output was not compilable. It did not have the problem with =indeck= that [[DecompilationJrpTest][JReversePro]] did. The output is cluttered with many unnecessary and distracting prefixes of " =this.= ". ---++ Sable Test Program For source code, see DecompilerSableTestSource. Here is the result for function =f= (as produced by ClassCracker):
  public static void f( short locVar_0)
  {
	 Object locVar_1;
	 Object locVar_2;
	 Object locVar_3;
	 int locVar_4;

	 if( locVar_0 > 10 )  {
		locVar_2 = (new Rectangle( locVar_0, locVar_0 ));
		locVar_4 = locVar_2.isFat(  );
		locVar_3 = locVar_2;
	 }
	 else {
		locVar_1 = (new Circle( locVar_0 ));
		locVar_4 = locVar_1.isFat(  );
		locVar_3 = locVar_1;
	 }
	 if( locVar_4 == 0 )  {
		locVar_3.draw(  );
	 }
  }
No attempt has been made to type the references; they are all of type =Object=, and there are't even casts to allow the code to recompile. Even the boolean is typed as an =int= (other decompilers are able to type it as a =boolean=, presumably from the return type of =isFat=. Using the "batch mode" of ClassCracker on all 4 class files at once did not produce any improvement. ---++ Optimised Bytecodes For source code, see DecompilerOptimisedTestSource. Output from ClassCracker for this code was (method =f= only):
  public static void f( short locVar_0)
  {
	 Object locVar_1;

	 if( locVar_0 > 10 )  {
		locVar_1 = (  );
		locVar_0 = locVar_1.isFat(  );
		locVar_1 = locVar_1;
	 }
	 else {
		locVar_1 = (  );
		locVar_0 = locVar_1.isFat(  );
		locVar_1 = locVar_1;
	 }
	 if( locVar_0 == 0 )  {
		locVar_1.draw(  );
	 }
  }
All decompilers apart from [[DecompilationDava][Dava]] seem to have trouble with this code. In addition to the above problems, the constructors for the Rectangle and Circle objects are missing. ---++ Simple control flow For source code, see DecompilerControlFlowTestSource. ClassCracker produces: public int foo( int locVar_1, int locVar_2) { Object locVar_3; L0: { // this line may need to be moved but within the same indentation depth break L0; try { while( locVar_1 < locVar_2 ) { locVar_2 ++; locVar_1 = ( locVar_2 / locVar_1 ); } } } catch( RuntimeException locVar_3 ) { locVar_1 = 10; break L0; } return locVar_2; } This code doesn't even compile. The first =break L0= is clearly wrong, and neither of the =break= statements is in a loop. The compiler (Sun's =javac=) does not consider that the =try= and =catch= are connected. ---++ Exceptions For source code, see DecompilerExceptionTestSource. Here is Class Cracker's output: public void foo( ) { L1: { // this line may need to be moved but within the same indentation depth System.out.println( "a" ); try { System.out.println( "b" ); try { System.out.println( "c" ); } catch( RuntimeException this ) { System.out.println( "g" ); break L1; } System.out.println( "d" ); } catch( Exception this ) { System.out.println( "e" ); } } System.out.println( "f" ); } This looks somewhat promising, but fails for a number of reasons. The output doesn't compile, because =this= can't be used as an exception variable. If an =Exception= occurs in block =b=, flow will go to =e=, which is not original program behaviour. Also, an Exception in =c= does not go to =e= as it should. ---++ Conclusion The GUI for this product takes a little getting used to, but after a while it becomes natural. It seems very fast for a Java based decompiler, but that could be because it only decompiles the first 5 methods (only in the demo version, obviously). Unfortunately, the decompiler, despite now being in its third version, needs some work to cater for the tricky cases covered in these tests. CategoryDecompilation