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.

4

5 回答 5

9

你好,

回复晚了非常抱歉。我从http://www.reversed-java.com/fernflower/forum?threadfolder=2_DE复制我的答案

您的问题实际上是一个众所周知的问题。让我们来看看:

1) 纯字节码不包含任何有关对象变量类型的信息,因此在第一遍中,s1 和 s2 被声明为 Object。

2)反编译器正在努力为每个变量分配可能的最佳类型(= Fernflower 中实现的“最窄类型原则”)。因此 s1 和 s2 被正确识别为 String 的实例。

3) doPrint 的调用为我们提供了指向正确方法的直接链接
private static void doPrint(Object s1)

4) 到目前为止一切正常,对吧?现在我们将一个字符串变量 s1 传递给一个函数,该函数需要一个对象。我们需要铸造它吗?你会认为不是这样,因为 Object 是 String 的超类型。然而我们这样做了——因为在同一个类中有另一个函数具有相同的名称和不同的参数签名。所以我们需要分析整个类来找出是否需要演员表。

5)一般来说,这意味着我们需要分析所有库中的所有引用类,包括java运行时。巨大的工作量!事实上,这个特性在 Fernflower 的一些 alpha 版本中实现了,但由于性能和内存损失,还没有在生产中实现。其他提到的反编译器在设计上缺乏这种能力。

希望我已经澄清了一些事情:)

于 2010-06-10T10:51:17.490 回答
5

Krakatau正确处理所有重载的方法,甚至是在原始类型上重载的方法,大多数反编译器都会出错。它总是将参数转换为被调用方法的确切类型,因此代码可能比必要的更混乱,但至少它是正确的。

披露:我是 Krakatau 的作者。

于 2014-03-10T16:38:39.413 回答
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.

于 2014-03-20T18:10:06.510 回答
3

用于反编译的 JadClipse Eclipse 插件还提供了 JODE 反编译器,您可能想尝试一下。Jad 放弃时我会使用它。

此外,Dava 反编译器使用了 Soot,我上次查看时,它在重构原始 Java 代码方面非常雄心勃勃。我没有尝试过你的例子,但你可能想看看。http://www.sable.mcgill.ca/dava/

于 2010-05-15T15:40:51.887 回答
1

添加到以前的答案:这是截至 2015 年 3 月的现代反编译器列表:

  • 南河四
  • 病死率
  • 京东
  • 蕨花

它们都支持重载方法。

您可以在线测试上述反编译器,无需安装并做出自己的明智选择。云中的 Java 反编译器: http ://www.javadecompilers.com/

于 2015-03-20T04:24:55.943 回答