Decompilation Nmi Test
Program-Transformation.Org: The Program Transformation Wiki
NMI Java Code Viewer
This is a commercial bytecode to Java decompiler and disassembler. It runs under Windows only, even though it was written in Java. I ran an evaluation copy of version 6.0. For brevity, NMI Java Code Viewer will be abbreviated to NJCV.
Fibo
For source, see
DecompilerFiboTestSource. Decompiled source from NJCV:
// NMI's Java Code Viewer 6.0
// http://www.njcv.tk
// Registered to Evaluation Copy
// Generated Sun Feb 23 2003 22:20:45
// Source File Name: Fibo.java
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 (save the strange local variable name). The output compiled and ran perfectly with no changes required.
Casting
For source, see
DecompilerCastingTestSource. Here is the output from NJCV:
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 quite compact, readable, very similar to the original, and is correct. No modifications were needed to recompile it.
Inner classes
For source, see
DecompilerInnerClassesTestSource. When decompiled with NJCV (turning on "inner class support"), the result is
public class Usa {
public class England {
public class Ireland {
public String name;
public void print_names() {
System.out.println(name);
}
public Ireland() {
name = "Dublin";
}
}
public String name;
public England() {
name = "London";
}
}
public String name;
public Usa() {
name = "Detroit";
}
}
It reproduced the inner classes correctly.
Deuces Wild
This is a 38K applet with two dimensional arrays of integers, some floating point code, and so on. It decompiled in a fraction of a second, with no errors, and recompiled and ran perfectly with no modifications. The arrays of strings are initialised naturally, e.g.
String cardrank[] = {
"Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack",
"Queen", "King", "Ace"
};
However, the string before this one also had ten strings on one line, producing a line that had well over a hundred columns. This is a very minor issue.
Sable Test Program
For source, see
DecompilerSableTestSource. Here is the result for function
f
(as produced by NJCV):
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();
}
As you can see, it had no trouble with the tricky
d
variable (here called
drawable
), and produced correct code which compiled without modification. However, the variable
d
of the original program becomes here
obj
of type
Object
, but it's cast as needed. Better decompilers infer that
obj
should be of type
Drawable
.
Optimised code.
For source, see
DecompilerOptimisedTestSource. This was the result from NJCV 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. When the above problems were corrected (e.g.
obj = new Rectangle(word0, word0)
, the code still did not compile, because NJCV failed to separate the ranges of the local variable (part
short
, part
boolean
).
Simple control flow
For source, see
DecompilerControlFlowTestSource. NJCV produces:
class foo {
foo() {
}
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;
}
public void main() {
foo(2, 3);
}
}
The "=while(true)" is preserved, but it has not gotten the continue or break correct.
Exceptions
NJVC produces this:
class foo {
foo() {
}
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;
}
public static void main() {
foo foo1 = new foo();
}
}
This code will obviously not compile, and it has made no attempt to separate the exception handling cases. In fact, the execptions have all gone.
Life
This version of Conway's Game of Life was compiled from Ada 95 source code. The applet is at
http://www.appletmagic.com/download/demo/LifeRect.html. NJCV seemed to be happy decompiling one class at a time, but it produced code with errors, e.g.
if(Result >= 0) goto _L2; else goto _L1
_L1:
int i = Result + 60;
i;
if(i < 0 || i > 59)
throw new IndexOutOfBoundsException();
return;
_L2:
This would appear to be a simple if-then-else. Note the line with just
"i;"
; what is missing here? At least the index bounds checking is natural (compare with JODE output, which has a
"do while(false)"
loop).
NJCV was able to type some parameters that JODE regarded as type errors (e.g. the parameter to
Directed_Copy
).
At least it never bombs out with runtime errors, so it would presumably be possible to edit the code to get it to work properly (with a fair bit of effort).
Conclusion
This is overall a good decompiler: very fast, good output quality.
It does have a few problems here and there,
but most of the time it's easy to correct.
CategoryDecompilation
--
MikeVanEmmerik - 23 Feb 2003