
Neste artigo, quatro decompiladores - Fernflower , CFR , Procyon e jadx - serão examinados e comparados de várias maneiras.
: . , ( 2019) Java-.
— (brontozyablik), Solar appScreener
— Solar appScreener — . Java-. : , . " 147- - -". , - .
: , ? : , , , , .
, :
: (UPD: Fernflower') , . , . , , .
: , .
. , , Fernflower — (analytical) . , , . ( , ). , .
:
- ( ) ;
- (
foreach
, try-with-resources
, etc).
, , , .
( — 2019 ). .
- Fernflower — , JetBrains. GitHub.
- CFR (0.146) — , , , , , "for fun". GitHub. .
- Procyon (0.5.36) — , Java. . Bitbucket.
- jadx (1.0.0) — , Dalvik, JVM. GitHub.
, , ,
.- JD-Core ( JD Project) — , ( gui).
UPD: GitHub. - Krakatau — , , Java 8.
- JAD — ( Java 5).
, Windows: Cavaj, DJ Java Decompiler, JBVD, AndroChef. , - - , . , , ...
:
, jadx Android. , jvm, dx. , jadx , jadx .
jadx DEX 37 , - , , .
Fernflower ( 16.09.19), CFR (0.146), Procyon (0.5.36) jadx (1.0.0). jadx .
, , — Fernflower, , Java 8. . Java — Procyon Java 9 , CFR ( Fernflower ).
java -jar fernflower.jar -dgs=1 -asc=1 -ind=" " <input-jar> <output-dir>
java -jar cfr-0.146.jar <input-jar> --outputpath <output-dir>
java -jar procyon-decompiler-0.5.36.jar -jar <input-jar> -o <output-dir>
./bin/jadx -d <output-dir> <input-jar> --show-bad-code
, .
Fernflower
, Intellij IDEA, .
, Fernflower — Intellij IDEA. ( , — Fernflower ).
, . master
3 ( 2019). , , , Intellij IDEA.
CFR
, ( ). , , -. (0.147), .
, .
Procyon
, (2019) . , . , , , .
jadx
, - . 20 2019 1.0.0. DVM .
, , . , .
jadx , 39 fernflower.jar
, , .
, : ( , CFR ); , ( , , ), .
, , , :
, , , ( CFR - ).
, .
, Fernflower, , 34 36 — variable <var> is already defined
. CFR . Procyon' (10 16) - , boolean
. - ( ).
, CFR — , 4 . 10 , 72 — . , "" CFR , , , .
: , - .
: , .
100 JAR- 5.2M (JAR-, , .class
).
— 15 JAR- 14M.
, . CFR , Fernflower . , 14M — .class
.
, .
. , , jadx, . jadx , Android-.

, .
for-each
FullInstructionSequence.java
for (ExceptionHandler handler : handlers) {
handler.from_instr = this.getPointerByAbsOffset(handler.from);
handler.to_instr = this.getPointerByAbsOffset(handler.to);
handler.handler_instr = this.getPointerByAbsOffset(handler.handler);
}
Fernflower for-each
. .
, handler
, - . , var3
, unchecked cast
:
ExceptionHandler handler;
for (Iterator var3 = handlers.iterator(); var3.hasNext(); handler.handler_instr = this.getPointerByAbsOffset(handler.handler)) {
handler = (ExceptionHandler)var3.next();
handler.from_instr = this.getPointerByAbsOffset(handler.from);
handler.to_instr = this.getPointerByAbsOffset(handler.to);
}
SSAConstructorSparseEx.java
varmaparr[varmaparr[1] == null ? 0 : 1]
Procyon'. , , , — , :
varmaparr[varmaparr[1] != null];
IFernflowerPreferences.java
public interface IFernflowerPreferences {
Map<String, Object> DEFAULTS = getDefaults();
static Map<String, Object> getDefaults() { ... }
}
, Procyon. default
, static
getDefaults()
, :
public interface IFernflowerPreferences {
public static final Map<String, Object> DEFAULTS = getDefaults();
default Map<String, Object> getDefaults() { return ... }
}
, .
, .unboxing
VarVersionPair.java
VarVersionsProcessor.java
public class VarVersionPair {
public final int var;
public final int version;
public VarVersionPair(int var, int version) {
this.var = var;
this.version = version;
}
public VarVersionPair(Integer var, Integer version) {
this.var = var;
this.version = version;
}
}
new VarVersionPair(ent.getKey().var , version.intValue() );
Fernflower
.
public class VarVersionPair {
public final int var;
public final int version;
public VarVersionPair(int var, int version) {
this.var = var;
this.version = version;
}
public VarVersionPair(Integer var, Integer version) {
this.var = var;
this.version = version;
}
}
new VarVersionPair(((VarVersionPair)ent.getKey()).var, version);
Procyon
public class VarVersionPair {
public final int var;
public final int version;
public VarVersionPair(final int var, final int version) {
this.var = var;
this.version = version;
}
public VarVersionPair(final Integer var, final Integer version) {
this.var = var;
this.version = version;
}
}
new VarVersionPair(ent.getKey().var , (int)version );
CFR
public class VarVersionPair {
public final int var;
public final int version;
public VarVersionPair(int var, int version) {
this.var = var;
this.version = version;
}
public VarVersionPair(Integer var, Integer version) {
this.var = var;
this.version = version;
}
}
new VarVersionPair(ent.getKey().var , (int)version );
dx + jadx
, .
public class VarVersionPair {
public final int var;
public final int version;
public VarVersionPair(int var, int version) {
this.var = var2;
this.version = version2;
}
public VarVersionPair(Integer var, Integer version) {
this.var = var.intValue();
this.version = version.intValue();
}
}
new VarVersionPair(((VarVersionPair) ent.getKey()).var , ((Integer) it.next()).intValue() );
Try-with-resources
ConsoleDecompiler.java
try (Writer out = new OutputStreamWriter(...)) {
<try-body>
}
catch (IOException ex) {
<catch-body>
}
Fernflower
, try-with-resources
. , try-catch
. (:
try {
Writer out = new OutputStreamWriter(...);
Throwable var8 = null;
try {
<try-body>
} catch (Throwable var18) {
var8 = var18;
throw var18;
} finally {
if (out != null) {
if (var8 != null) {
try {
out.close();
} catch (Throwable var17) {
var8.addSuppressed(var17);
}
} else {
out.close();
}
}
}
} catch (IOException var20) {
<catch-body>
}
CFR
: 0.142 try-with-resources
, 0.146 try
.
UPD: 0.147.
try {
try (OutputStreamWriter out = new OutputStreamWriter(...);){
out.write(content);
}
}
catch (IOException ex) {
<catch-body>
}
Procyon
try (final Writer out = new OutputStreamWriter(...)) {
out.write(content);
}
catch (IOException ex) {
<catch-body>
}
dx + jadx (with --show-bad-code
option)
jadx .
...
try {
Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
Throwable th = null;
<try-body>
if (out == null) {
return;
}
if (th != null) {
try {
out.close();
} catch (Throwable th2) {
th.addSuppressed(th2);
}
} else {
out.close();
}
} catch (IOException ex) {
<catch-body>
} catch (Throwable th3) {
r4.addSuppressed(th3);
}
ClassReference14Processor.java
graph.iterateExprents(exprent -> {
for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {
<body>
}
return 0;
});
Fernflower
. for-each
( for-each
).
graph.iterateExprents((exprentx) -> {
Iterator var3 = mapClassMeths.entrySet().iterator();
while(var3.hasNext()) {
Entry<ClassWrapper, MethodWrapper> ent = (Entry)var3.next();
<body>
}
return 0;
});
Procyon
for-each
( for-each
Procyon ). ent
iterator2
, , ent
effectively final
.
final Iterator<Map.Entry<ClassWrapper, MethodWrapper>> iterator2;
Map.Entry<ClassWrapper, MethodWrapper> ent;
graph.iterateExprents(exprent -> {
mapClassMeths.entrySet().iterator();
while (iterator2.hasNext()) {
ent = iterator2.next();
<body>
}
return 0;
});
CFR
graph.iterateExprents(exprent -> {
for (Map.Entry ent : mapClassMeths.entrySet()) {
<body>
}
return 0;
});
dx + jadx
jadx . .
throw new UnsupportedOperationException("Method not decompiled: org.jetbrains.java.decompiler.main.ClassReference14Processor.processClassRec(org.jetbrains.java.decompiler.main.ClassesProcessor$ClassNode, java.util.Map, java.util.Set):void");
for
SwitchInstruction.java
for (int i = 0, k = 0; i < len; i++, k++) {
if (<condition>) {
...
k++;
}
...
}
Fernflower
i
.
int i = 0;
for(int k = 0; i < len; ++k) {
if (<condition>) {
...
++k;
}
...
++i;
}
Procyon
for (int i = 0, k = 0; i < len; ++i, ++k) {
if (<condition>) {
...
++k;
}
...
}
CFR
( ).
int i = 0;
int k = 0;
while (i < len) {
if (<condition>) {
...
++k;
}
...
++i;
++k;
}
dx + jadx
, CFR.
int i = 0;
int k = 0;
while (i < len) {
if (<condition>) {
...
k++;
}
...
i++;
k++;
}
Generics
.
ConcatenationHelper.java
List<Exprent> lstOperands = new ArrayList<>();
Fernflower
ArrayList lstOperands = new ArrayList();
Procyon
final List<Exprent> lstOperands = new ArrayList<Exprent>();
CFR
ArrayList<Exprent> lstOperands = new ArrayList<Exprent>();
dx + jadx
List<Exprent> lstOperands = new ArrayList<>();
VarTypeProcessor.java
LinkedList<Statement> stack = new LinkedList<>();
stack.add(root);
stack.addAll(stat.getStats());
Fernflower
unchecked assignment
.
LinkedList<Statement> stack = new LinkedList();
stack.add(root);
stack.addAll(stat.getStats())
Procyon
, .
final LinkedList<Statement> stack = new LinkedList<Statement>();
stack.add(root);
stack.addAll((Collection<? extends Statement>)stat.getStats());
CFR
, Statement
<RootStatement>
.
LinkedList<RootStatement> stack = new LinkedList<RootStatement>();
stack.add(root);
stack.addAll(stat.getStats());
dx + jadx
LinkedList<Statement> stack = new LinkedList<>();
stack.add(root);
stack.addAll(stat.getStats());
Statement.java
protected HashSet<Statement> continueSet = new HashSet<>();
...
continueSet.addAll(st.buildContinueSet());
Fernflower
protected HashSet<Statement> continueSet = new HashSet<>();
...
this.continueSet.addAll(st.buildContinueSet());
Procyon
.
protected HashSet<Statement> continueSet;
...
public Statement() {
this.continueSet = new HashSet<Statement>();
...
}
...
this.continueSet.addAll((Collection<?>)st.buildContinueSet());
CFR
protected HashSet<Statement> continueSet = new HashSet<>();;
...
this.continueSet.addAll(st.buildContinueSet());
dx + jadx
protected HashSet<Statement> continueSet;
...
this.continueSet.addAll(st.buildContinueSet());
jadx dex
, jadx Android — AntennaPod ( ).
.jadx . , , :
private static Context context;
public static void init(Context context) {
UpdateManager.context = context;
...
}
:
private static Context context;
public static void init(Context context) {
context = context;
...
}
jadx , , :
(item1, item2) -> compareLong(item1.timePlayed, item2.timePlayed)
:
public final class $$Lambda$DBReader$J14FiokVfxZ2H5XUZEtHQOEEq_0 implements Comparator {
public static final
$$Lambda$DBReader$J14FiokVfxZ2H5XUZEtHQOEEq_0 INSTANCE
= new $$Lambda$DBReader$J14FiokVfxZ2H5XUZEtHQOEEq_0();
private $$Lambda$DBReader$J14FiokVfxZ2H5XUZEtHQOEEq_0() { }
public final int compare(Object obj, Object obj2) {
return DBReader.compareLong(((StatisticsItem)obj).timePlayed,
((StatisticsItem)obj2).timePlayed);
}
}
. :
public Feed(...) {
this(id, lastUpdate, title, null, link,
description, paymentLink, author,
language, type, feedIdentifier, imageUrl,
fileUrl, downloadUrl, downloaded,
new FlattrStatus(), false, null, null, false);
}
:
public Feed(...) {
long j = id;
String str = lastUpdate;
String str2 = title;
String str3 = link;
String str4 = description;
String str5 = paymentLink;
String str6 = author;
String str7 = language;
String str8 = type;
String str9 = feedIdentifier;
String str10 = imageUrl;
String str11 = fileUrl;
String str12 = downloadUrl;
boolean z = downloaded;
FlattrStatus flattrStatus = r5;
FlattrStatus flattrStatus2 = new FlattrStatus();
this(j, str, str2, null, str3,
str4, str5, str6,
str7, str8, str9, str10,
str11, str12, z,
flattrStatus, false, null, null, false);
}
, , jadx . :
URL url = new URI(BASE_SCHEME, BASE_HOST,
String.format("/api/2/tags/%d.json", count), null).toURL();
Request.Builder request = new Request.Builder().url(url);
String response = executeRequest(request);
JSONArray jsonTagList = new JSONArray(response);
:
JSONArray jsonTagList
= new JSONArray(executeRequest(new Builder().url(
new URI(BASE_SCHEME,
this.BASE_HOST,
String.format("/api/2/tags/%d.json",
new Object[]{Integer.valueOf(count)}), null).toURL())));
, jadx :
final String action = intent.getStringExtra(ARG_ACTION);
if (action != null) {
switch(action) {
case ACTION_SYNC:
<code1>
case ACTION_SYNC_SUBSCRIPTIONS:
<code2>
case ACTION_SYNC_ACTIONS:
<code3>
default:
<code4>
}
}
:
String action = intent.getStringExtra(ARG_ACTION);
if (action != null) {
Object obj = -1;
int hashCode = action.hashCode();
if (hashCode != -1744995379) {
if (hashCode != 29421060) {
if (hashCode == 1497029227 && action.equals(ACTION_SYNC_ACTIONS)) {
obj = 2;
}
} else if (action.equals(ACTION_SYNC_SUBSCRIPTIONS)) {
obj = 1;
}
} else if (action.equals(ACTION_SYNC)) {
obj = null;
}
switch (obj) {
case null:
<code1>
case 1:
<code2>
case 2:
<code3>
default:
<code4>
}
}
if(item != null) {
return item.getId() == id;
}
...
FeedItem feedItem = this.item;
boolean z = true;
if (feedItem != null) {
if (feedItem.getId() != id) {
z = false;
}
return z;
}
, , , jadx, , . , , jadx 15 switch-case if-else , .
:
CFR
( for-each
, try-with-resources
, ), ( ). CFR .
— , , , ( , , , ; ).
Procyon
, . - CFR Java 9 . Procyon ( ).
Fernflower
. ( ). , Fernflower Intellij IDEA, , .
jadx
( ) , Android. , ( , ). (, try-with-resources
) DVM 37 . JAR .
P.S. , : . , , , , .