Decompilation Jad Test

Program-Transformation.Org: The Program Transformation Wiki

Jad Java Decompiler Simple Tests

This page performs some tests on JAD version 1.5.8e. Output has been trimmed slightly for ease of comparison to the original source.

Fibo

For source, see DecompilerFiboTestSource. Decompiled output from Jad:

// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
import java.io.PrintStream;

class Fibo
{
    Fibo()
    {
    }

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

    public static void main(String args[])
        throws Exception
    {
        int i = 0;
        try
        {
            i = Integer.parseInt(args[0]);
        }
        catch(Exception exception)
        {
            System.out.println("Input error");
            System.exit(1);
        }
        int j = fib(i);
        System.out.println("fibonacci(" + i + ") = " + j);
    }
}

As you can see, the decompilation is almost identical to the source.

Casting

For source, see DecompilerCastingTestSource. Here is the output from Jad:

// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
import java.io.PrintStream;
public class Casting
{
    public Casting()
    {
    }

    public static void main(String args[])
    {
        for(char c = '\0'; c < 128; c++)
            System.out.println("ascii " + (int)c + " character " + c);
    }
}
As you can see, it's readable, similar to the original, and is correct. No modifications were needed to recompile it.

Inner classes

For source, see DecompilerInnerClassesTestSource. When decompiled with Jad, the result is

// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
import java.io.PrintStream;
public class Usa
{
    public class England
    {
        public class Ireland
        {
            public void print_names()
            {
                System.out.println(name);
            }
            public String name;
            public Ireland()
            {
                name = "Dublin";
            }
        }
        public String name;
        public England()
        {
            name = "London";
        }
    }
    public Usa()
    {
        name = "Detroit";
    }
    public String name;
}

I'm no inner classes expert, but this looks right to me, even though it looks different to the original source code.

Deuces Wild

This is a 38K applet with two dimensional arrays of integers, some floating point code, and so on. It decompiled in about 0.8 seconds, with no errors, and recompiled and ran perfectly with no modifications. The data members appear at the end of the class definition, which I find a little irritating. The arrays of strings are initialised fairly naturally, e.g.

    String cardrank[] = {
        "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine",
"Ten", "Jack",
        "Queen", "King", "Ace"
    };
    String cardsuit[] = {
        "clubs", "diamonds", "hearts", "spades"
    };

Sable Test Program

For source, see DecompilerSableTestSource. Here is the result for function f (as produced by Jad):

    public static void f(short word0)
    {
        Object obj;
        boolean flag;
        if(word0 > 10)
        {
            Rectangle rectangle = new Rectangle(word0, word0);
            flag = rectangle.isFat();
            obj = rectangle;
        } else
        {
            Circle circle = new Circle(word0);
            flag = circle.isFat();
            obj = circle;
        }
        if(!flag)
            ((Drawable) (obj)).draw();
    }

It decided to type the reference that was d in the original source as type Object (name obj). This is not ideal, since it requires a cast when used. The cast was at least correctly inserted. Other decompilers such as Jode and Dava can correctly type the "=d" variable as type Drawable. It produced correct code which compiled without modification.

Optimised code

For source, see DecompilerOptimisedTestSource. This was the result from Jad for the method f:

    public static void f(short word0)
    {
        Object obj;
        if(word0 > 10)
        {
            obj = JVM INSTR new #37  <Class Rectangle>;
            ((Rectangle) (obj)).Rectangle(word0, word0);
            word0 = ((Rectangle) (obj)).isFat();
            obj = obj;
        } else
        {
            obj = JVM INSTR new #15  <Class Circle>;
            ((Circle) (obj)).Circle(word0);
            word0 = ((Circle) (obj)).isFat();
            obj = obj;
        }
        if(word0 == 0)
            ((Drawable) (obj)).draw();
    }

As you can see, it is confused with the two constructors. The fix is moderately obvious; there is no chance that the error will slip by the java compiler, at least.

Simple control flow

For source, see DecompilerControlFlowTestSource. Jad produces:

    public int foo(int i, int j)
    {
        while(true)
            try
            {
                while(i < j)
                    i = j++ / i;
                break MISSING_BLOCK_LABEL_28;
            }
            catch(RuntimeException runtimeexception)
            {
                i = 10;
            }
        return j;
    }

This result does not compile, and is missing several statements.

Exceptions

For source, see DecompilerExceptionTestSource. Here is Jad's output:

    public void foo()
    {
        System.out.println("a");
        System.out.println("b");
        System.out.println("c");
        break MISSING_BLOCK_LABEL_39;
        this;
        System.out.println("g");
        break MISSING_BLOCK_LABEL_59;
        System.out.println("d");
        break MISSING_BLOCK_LABEL_59;
        this;
        System.out.println("e");
        System.out.println("f");
        return;
    }
JAD has completely given up on any exception handling. Interestingly, in the paper mentioned above, Jad is reported as having one try and catch clause. Obviously, this output is nowhere near correct, and will not even compile.

Life

This applet is originally compiled in Ada 95. The applet is at http://www.appletmagic.com/download/demo/LifeRect.html. When Jad was run on this file, it admitted that it could not completely decompile method run. It failed this relatively easy control flow code:

        if(false) goto _L2; else goto _L1
_L1:
        int I = 1;
_L3:
        double d;
        ...
        I++;
        if(I > 180) goto _L2; else goto _L3
_L2:
which Jode correctly decompiled as follows:
    if (true) {
        int I = 1;
        do {
        double d = ...
        I++;
        } while (I <= 180);
    }
Granted, the if (true) is strange, but should not throw a decompiler. Similar problems occured with other classes of this applet.

Conclusion

This is a good decompiler: extremely fast, reasonable output quality. It does have a problem with some optimised code, as shown above, but most of the time it's easy to correct. None of the decompilers I have tested so far (8 in total) has passed all the tests. JODE, Dava, and Jad are the top three.

CategoryDecompilation