Декомпиляция Java с JAD - ограничения
Я пытаюсь декомпилировать пару jar-файлов, используя JAD в Java (я также пробовал JD-GUI с еще меньшей удачей), но получаю довольно много ошибок. Один тип (легко исправить), кажется, с внутренними классами, но я также нашел этот фрагмент кода:
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;
}
который используется следующим образом:
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;
}
Любая идея, что эта конструкция изначально могла быть?
3 ответа
Я думаю, что это таблица переключателей для перечисления. Он переводит произвольное порядковое значение enum в число 0..n, это позволяет улучшить производительность оператора switch.
ОБНОВЛЕНИЕ Просто понял это! Проблема в том, что код, использующий перечисление, может быть скомпилирован отдельно от самого перечисления. Таким образом, он не знает во время компиляции порядковые значения, поэтому он не может создать правильную операцию переключения таблиц. Вот почему он вводит ленивую структуру SWITCH_TABLE, чтобы отобразить доступные в настоящий момент порядковые значения для локальных чисел переключателя табличного переключателя.
Выглядит как заявление о переключении на enum. Взгляните на класс enum, который неявно расширяется. Имеет ordinal
метод, который используется для переключения. Там, вероятно, некоторые OrderType
enum с константами LIMIT, MARKET, STOP и TAKE.
РЕДАКТИРОВАТЬ: На самом деле, я думаю, было бы неплохо больше информации. Для таких вещей, как перечисления, используется дым и зеркала. Константа перечисления получает некоторый порядковый номер за экранами. Этот порядковый номер - то, что фактически используется в группе конструкций. При переключении на экземпляр enum компилятор фактически создает переключение над int (известной конструкцией, которая уже давно существует) с порядковым номером в качестве входных данных.
То, что происходит в ваших двух кодовых блоках, таково: первый устанавливает "таблицу" (на самом деле просто массив) для порядковых номеров перечисления, если это еще не произошло. Там нулевая проверка. Если таблица пуста, она перейдет к метке _L2
сделать грунтовку. В противном случае он переходит на метку _L1
это просто возвращается. Второй блок кода (оператор фактического переключения) выполняет переключение на int. Int получается из таблицы путем получения элемента по индексу, который соответствует порядковому номеру константы enum.
Это кажется немного странным, но это формирует некоторую косвенность между порядковыми номерами перечисления и значениями, используемыми внутри коммутатором.
Теперь причина того, что все это выглядит так низкоуровнево, вместо простого просмотра переключателя перечисления, заключается в том, что перечисления были введены в JDK 1.5, но JAD некоторое время не работал и реально поддерживает только декомпиляцию исходного кода до версии 1.4. Видя, как перечисления были реализованы с использованием конструкций, доступных в 1.4, декомпиляция действительно работает, но JAD ничего не знает о перечислениях, и поэтому не пытается представить это более разборчивым образом.
Вот как, вероятно, выглядел этот второй блок кода:
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;
}
При использовании метода getDeclaredFields() API отражения на Android, если анализируемый тип содержит переключатель enum, в котором enum объявлен в другом классе, одним из возвращаемых полей будет ленивая структура SWITCH_TABLE, называемая чем-то вроде $SWITCH_TABLE$com$company$package$ClassName$EnumName
,
Я использовал отражение для автоматического сопоставления полей класса с ContentValues
перед сохранением в базу данных SQLite и потратил полдня на это, пытаясь выяснить, почему массив, содержащий имена столбцов, всегда был отключен на единицу.