Is there any Java Decompiler that can correctly decompile calls to overloaded methods? [closed]
Asked Answered
I

5

9

Consider this (IMHO simple) example:

public class DecompilerTest {
    public static void main(String[] args) {
        Object s1 = "The", s2 = "answer";
        doPrint((Object) "You should know:");
        for (int i = 0; i < 2; i++) {
            doPrint(s1);
            doPrint(s2);
            s1 = "is";
            s2 = new Integer(42);
        }
        System.out.println();
    }

    private static void doPrint(String s1) {
        System.out.print("Wrong!");
    }

    private static void doPrint(Object s1) {
        System.out.print(s1 + " ");
    }
}

Compile it with source/target level 1.1 without debug information (i.e. no local variable information should be present) and try to decompile it. I tried Jad, JD-GUI and Fernflower, and all of them got at least one of the call wrong (i. e. the program printed "Wrong!" at least once)

Is there really no java decompiler that can infer the right casts so that it will not call the wrong overload?

Edit: Target level 1.1 so that there is none of that Java6-specific fast-validation information present. That might give the decompiler a clue that s1 has been declared as Object and not as String. Decompilers should be able to decompile the code even without this information (not necessarily get the original variable type, but show the same behaviour), especially since lots of obfuscators strip it as well.

What decompilers got wrong:

  • They missed the cast to (Object) in the first call.
  • They inferred the type of s1 to be String, but forgot to add a cast to the call to doPrint (so that the String version is called instead of the Object version).
  • One crappy one (I have not even listed) even infers the type of s2 to be String, causing uncompilable code.

In any case, this code never calls the String overload, but the decompiled code did.

Imprinting answered 15/5, 2010 at 13:1 Comment(1)
What do you mean "they got the call wrong"? What source do they generate after decompiling?Alkalinity
O
5

Krakatau correctly handles all overloaded methods, even methods overloaded on primitive types, which most decompilers get wrong. It always casts arguments to the exact type of the called method, so the code may be more cluttered than necessary, but at least it is correct.

Disclosure: I am the author of Krakatau.

Optime answered 10/3, 2014 at 16:38 Comment(0)
U
9

Hallo mihi,

sorry for the late response. I'm copying my answer from http://www.reversed-java.com/fernflower/forum?threadfolder=2_DE

Your problem is actually a well known one. Let's see:

1) Pure bytecode doesn't contain any information about the type of object variables, so in the first pass s1 and s2 are declared as Object.

2) Decompiler is trying hard to assign the best possible type to each variable (= "narrowest type principle" implemented in Fernflower). So s1 and s2 are correctly identified as instances of String.

3) Invocation of doPrint give us a direct link to the correct method
private static void doPrint(Object s1)

4) Everything OK so far, right? Now we have got a String variable s1 passed to a function, which expects an Object. Do we need to cast it? Not as such, you would think, as Object is a super-type of String. And yet we do - because there is another function within the same class with the same name and a different parameter signature. So we need to analyse the whole class to find out, whether a cast is needed or not.

5) Generally speaking, it means we need to analyse ALL referenced classes in ALL libraries including the java runtime. A huge load of work! Indeed, this feature was implemented in some alpha version of Fernflower, but have not made it in the production yet because of performance and memory penalty. Other mentioned decompilers lack this ability by design.

Hope I have clarified things a bit :)

Unwarranted answered 10/6, 2010 at 10:51 Comment(4)
Yes, I know the general problem (that's why it was not hard for me to reduce a much longer class to the sample above). But, as I understand it, if you do not have class information about the target class, you can always add a (superfluous) cast to the exact type of the parameter, can't you? You can still use Eclipse's quickfix to remove all superfluous casts later. (i. e. I'd prefer correctness to beauty when decompiling code)Imprinting
You are right, it could be done. The only downside would be that we generate some very ugly code with all these unnecessary casts. I'll implement an option for this behaviour, thank you for the suggestion.Unwarranted
"Pure bytecode doesn't contain any information about the type of object variables"Dissenter
"Pure bytecode doesn't contain any information about the type of object variables" - wrong. Types are derived from typed data sources (arguments, fields) and operations and method calls (all all them are typed). Also stack map attributes are supplied to method codes. Moreover, bytecode verifier prevents loading classfiles with incorrectly typed local variables.Dissenter
O
5

Krakatau correctly handles all overloaded methods, even methods overloaded on primitive types, which most decompilers get wrong. It always casts arguments to the exact type of the called method, so the code may be more cluttered than necessary, but at least it is correct.

Disclosure: I am the author of Krakatau.

Optime answered 10/3, 2014 at 16:38 Comment(0)
U
3

The JadClipse Eclipse plugin for decompiling also provides the JODE decompiler, which you may want to try. I use it when Jad gives up.

Also the Dava decompiler uses Soot which - last time i looked - was very ambitious in reconstructing the original Java code. I have not tried with your example, but you may want to have a look. http://www.sable.mcgill.ca/dava/

Uraninite answered 15/5, 2010 at 15:40 Comment(3)
Dava failed like the others. Jode worked, but since Jode tends to crash on almost every program that ran through any obfuscator, this does not really help me :( Guess I asked the wrong question. ;)Imprinting
Downvote and accept the answer? That was new :)Goodnatured
changed my accepted answer if you don't like to be downvoted and accepted :) (I first downvoted you because your suggestions did not work either but then decided that from all downvoted answers your was the best) - btw where can you see that it was me who both downvoted and accepted?Imprinting
R
3

Procyon should handle overloaded method invocations correctly. Like Krakatau, Procyon initially inserts casts for every method argument that doesn't exactly match the target method. However, most of these will be removed during a later phase of decompilation that identifies and eliminates redundant casts. Procyon will only remove casts on call arguments if it can verify that doing so will not result in the call binding to a different method. For example, if the .class declaring the method cannot be resolved, it won't attempt to remove the casts at all, because it has no way of knowing what overloads might conflict.

Robby answered 20/3, 2014 at 18:10 Comment(0)
D
1

Adding to the previous answers: Here's a list of modern decompilers as of March 2015:

  • Procyon
  • CFR
  • JD
  • Fernflower

ALL of them support overloaded methods.

You may test above mention decompilers online, no installation required and make your own educated choice. Java decompilers in the cloud: http://www.javadecompilers.com/

Doublestop answered 20/3, 2015 at 4:24 Comment(2)
Not sure if JD and JDCore are the same, but for the record: JDCore from that website fails decompiling the sample from this questionImprinting
JD and JDCore are the same. It is bad it failed. Hope other decompilers suceeded?Doublestop

© 2022 - 2024 — McMap. All rights reserved.