Decompiling java with JAD - limitations

3.3k views Asked by At

I am trying to decompile a couple of jar files using JAD in Java (I tried JD-GUI as well with even less luck), but I get quite a lot of errors. One type (easy to fix) seems to be with internal classes, but I also found this bit of code:

static int[] $SWITCH_TABLE$atp$com$OrderType()
{
    $SWITCH_TABLE$atp$com$OrderType;
    if($SWITCH_TABLE$atp$com$OrderType == null) goto _L2; else goto _L1
_L1:
    return;
_L2:
    JVM INSTR pop ;
    int ai[] = new int[OrderType.values().length];
    try
    {
        ai[OrderType.LIMIT.ordinal()] = 2;
    }
    catch(NoSuchFieldError _ex) { }
    try
    {
        ai[OrderType.MARKET.ordinal()] = 1;
    }
    catch(NoSuchFieldError _ex) { }
    try
    {
        ai[OrderType.STOP.ordinal()] = 3;
    }
    catch(NoSuchFieldError _ex) { }
    try
    {
        ai[OrderType.TAKE.ordinal()] = 4;
    }
    catch(NoSuchFieldError _ex) { }
    return $SWITCH_TABLE$atp$com$OrderType = ai;
}

which is used as follows:

switch($SWITCH_TABLE$atp$com$OrderType()[co.getOrderType().ordinal()])
        {
        case 1: // '\001'
            order = new Order(userID, null, co.getOrderType(), co.getOrderSide(), co.getOrderID(), co.getOrderSecurity(), co.getOrderQuantity(), broker);
            break;

        case 2: // '\002'
            order = new Order(userID, null, co.getOrderType(), co.getOrderSide(), co.getOrderPrice(), co.getOrderID(), co.getOrderSecurity(), co.getOrderQuantity(), broker);
            break;

        case 3: // '\003'
            order = new Order(userID, null, co.getOrderType(), co.getOrderSide(), co.getOrderPrice(), co.getOrderID(), co.getOrderSecurity(), co.getOrderQuantity(), broker);
            break;
        }

Any idea what this construct originally could have been?

3

There are 3 answers

1
G_H On

Looks to me like a switch statement on an enum. Take a look at the enum class, which enums implicitly extend. It has the ordinal method that's being used to switch over. There's probably some OrderType enum with constants LIMIT, MARKET, STOP and TAKE.

EDIT: Actually, I'm guessing some more info would be nice. There's some smoke and mirrors being used for stuff like enums. An enum constant gets some ordinal number behind the screens. This ordinal number is what's actually used in a bunch of constructs. When switching over an enum instance, the compiler actually creates a switch over an int (a well-known construct that's been around for a while) with the ordinal number as input.

What happens in your two code blocks is this: the first one sets up a "table" (actually just an array) for the enum ordinals if this didn't happen yet. There's a null check. If the table is null, it'll jump to label _L2 to do the priming. Otherwise, it jumps to label _L1 that simply returns. The second code block (the actualy switch statement) does a switch on an int. The int is obtained from the table by getting the element at the index that corresponds with the enum constant's ordinal number.

It seems a bit odd, but this forms some sort of indirection between the enum ordinals and the values used internally by the switch.

Now, the reason this all looks so low-level instead of simply seeing a switch on an enum is that enums were introduced in JDK 1.5, but JAD has been out of maintenance for a while and only really supports decompilation of source up to 1.4. Seeing how enums were implemented using constructs available in 1.4, the decompilation does actually work, but JAD has no knowledge of enums and as such doesn't make an effort to present this in a more legible manner.

Here's what that second code block probably looked like:

switch(co.getOrderType()) { //co.getOrderType() gets the OrderType of some variable
    case MARKET : order = new Order(userID, null, co.getOrderType(), co.getOrderSide(), co.getOrderID(), co.getOrderSecurity(), co.getOrderQuantity(), broker);
                  break;
    case LIMIT : order = new Order(userID, null, co.getOrderType(), co.getOrderSide(), co.getOrderPrice(), co.getOrderID(), co.getOrderSecurity(), co.getOrderQuantity(), broker);
                 break;
    case STOP : order = new Order(userID, null, co.getOrderType(), co.getOrderSide(), co.getOrderPrice(), co.getOrderID(), co.getOrderSecurity(), co.getOrderQuantity(), broker);
                break;
}
1
kan On

I think it's tableswitch for an enum. It translates the arbitrary enum ordinal value to number 0..n, it allows improve performance of the switch statement.

UPDATE Just understood it! The problem is - the code which uses the enum could be compiled separately from the enum itself. So it doesn't know at compile time the ordinal values, so it cannot construct a proper tableswitch op. So, that's why it introduces the lazy SWITCH_TABLE structure to map currently available ordinal values to the local tableswitch int numbers.

0
Jeshurun On

When using the getDeclaredFields() method of the reflection API on Android, if the type being introspected contains an enum switch where the enum is declared in another class, one of the returned fields will be the lazy SWITCH_TABLE structure, called something like $SWITCH_TABLE$com$company$package$ClassName$EnumName.

I was using reflection to automatically map fields of a class to ContentValues before saving to the SQLite database, and just spent half a day on this trying to figure out why the array holding the column names was always off by one.