java-decompiler / jd-core Goto Github PK
View Code? Open in Web Editor NEWJD-Core is a JAVA decompiler written in JAVA.
License: GNU General Public License v3.0
JD-Core is a JAVA decompiler written in JAVA.
License: GNU General Public License v3.0
With the jcenter shutdown scheduled for the 1st May 2021. You may want to publish the artifacts on maven central.
1.1.3+, Commit 7f01508
Compiler: javac 11.0.5
It appears the local var type is chosen without checking if it matches further usage.
Might be the same as #25.
Source:
class Test {
void useInt(int i) { }
void testVarType() {
int i = 1;
useInt(i);
}
}
Decompiled output:
class Test {
void useInt(int paramInt) {}
void testVarType() {
boolean bool = true;
useInt(bool);
}
}
Hello 大神:
Source code
1: private Object obj1 = new Object();
2: public init(){}
3:
4:public void test(){}
5: private Object obj2 = new Object();
java-decompiler class code
1: private Object obj1 = new Object();
2: public init(){}
3:
4
5: private Object obj2 = new Object(); 4:public void test(){}
Is there any setting that would allow to show fully qualified class name, e.g.:
import com.company.Printer
import com.company2.utils.Printer
...
com.company.Printer printer = new com.company.Printer();
.....
com.company2.utils.Printer printer2 = new com.company2.utils.Printer();
1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232, javac 11.0.5
It appears for loops over int
s are converted to loops over byte
s in some cases. However if that happens the incremented index is not cast accordingly when calling methods.
Source:
class ForLoopTest {
void test(int i) { }
void test(byte b) { }
void test() {
for (int i = 0; i < 10; i++) {
test(i);
}
}
}
Decompiled output:
class ForLoopTest {
void test(int paramInt) {}
void test(byte paramByte) {}
void test() {
for (byte b = 0; b < 10; b++)
// Calls the wrong method
test(b);
}
}
src utf-8
package com.glueframework.complier;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class InitTable {
public static Boolean crateTable(String tableName,String[] items) throws SQLException {
String urlString="jdbc:h2:D:/springboot/h2/test";
String usernameString="admin";
String password="";
Statement stmt = null;
Connection connection=null;
try {
connection=DriverManager.getConnection(urlString, usernameString, password);
DatabaseMetaData meta = connection.getMetaData();
ResultSet rsTables = meta.getTables(null, null, tableName,
new String[] { "TABLE" });
if (!rsTables.next()) {
stmt = connection.createStatement();
StringBuilder sql = new StringBuilder();
sql.append(" CREATE TABLE ");
if (null!=tableName&&tableName.length()>0) {
sql.append(tableName);
}
if (items != null && items.length > 0) {
sql.append(" ( ");
for (int i = 0;i < items.length;i++) {
sql.append(items[i]);
sql.append(" VARCHAR(5000), ");
}
sql.append("PRIMARY KEY(key,environment,groupId,artifactId)) ");
}
stmt.execute(sql.toString());
}
rsTables.close();
return true;
} finally {
stmt.close();
connection.close();
}
}
public static void main(String[] args) throws SQLException {
// java -jar h2*.jar org.h2.tools.Server -web -webPort 8082 -browser
//org.h2.tools.Server.main("-web", "-webPort", "8082", "-browser");
String[] items= {"value","key","environment","groupId","artifactId","version","createTime","updateTime","flag"};
String tablename="ConfigerBean";
crateTable(tablename,items);
}
protected static void getClassss() throws ClassNotFoundException {
Class<?> forName = Class.forName("com.glueframework.complier.Test");
System.out.println(forName);
}
}
win7 Notepad open class
漱壕 4 ? �� $com/glueframework/complier/InitTable� �� �java/lang/Object� �<init>� �()V� �Code
� � �� �LineNumberTable� �LocalVariableTable� �this� &Lcom/glueframework/complier/InitTable;�
crateTable� :(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Boolean;�
Exceptions� �� �java/sql/SQLException� �� �jdbc:h2:D:/springboot/h2/test� �� �admin� ��
� �� �� �java/sql/DriverManager � ��
getConnection� M(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;� "� !� �java/sql/Connection # $� �getMetaData� �()Ljava/sql/DatabaseMetaData;� &� �java/lang/String� (� �TABLE� * ,� +� �java/sql/DatabaseMetaData - .� getTables� _(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/ResultSet;� 0 2� 1� �java/sql/ResultSet 3 4� �next� �()Z� 6 7 8� �createStatement� �()Ljava/sql/Statement;� :� �java/lang/StringBuilder
9 � =� � CREATE TABLE
9 ? @ A� �append� -(Ljava/lang/String;)Ljava/lang/StringBuilder;
% C D E� �length� �()I� G� � ( � I� � VARCHAR(5000), � K� 1PRIMARY KEY(key,environment,groupId,artifactId))
9 M N O� �toString� �()Ljava/lang/String;� Q S� R� �java/sql/Statement T U� �execute� �(Ljava/lang/String;)Z� 0 W X �� �close
Z \� [� �java/lang/Boolean ] ^� �valueOf� �(Z)Ljava/lang/Boolean;� Q W� W� tableName� �Ljava/lang/String;� �items� �[Ljava/lang/String;� urlString� �usernameString� �password� �stmt� �Ljava/sql/Statement;�
connection� �Ljava/sql/Connection;� �meta� �Ljava/sql/DatabaseMetaData;� �rsTables� �Ljava/sql/ResultSet;� �sql� �Ljava/lang/StringBuilder;� �i� �I�
StackMapTable� d� w� �java/lang/Throwable� �main� �([Ljava/lang/String;)V� {� �value� }� �key� �� �environment� ? �groupId� ?
artifactId� ? �version� ?
createTime� ?
updateTime� ? �flag� ? ConfigerBean
� ? � �� �args� tablename�
getClassss� ? java/lang/ClassNotFoundException� ? �com.glueframework.complier.Test
? ? ? �java/lang/Class ? ? �forName� %(Ljava/lang/String;)Ljava/lang/Class; ? ? ? �java/lang/System ? ? �out� �Ljava/io/PrintStream;
? ? ? �java/io/PrintStream ? ? �println� �(Ljava/lang/Object;)V� �Ljava/lang/Class;� �LocalVariableTypeTable� �Ljava/lang/Class<*>;�
SourceFile� �InitTable.java ! � � � � � � � � / � � �*? �? �
� � � � � �
� � � � � � � � �S �
?�M��N��:��:��:�,-��? �:���? �� :�����*�? %Y��'S? )� :���? /� ? w��? 5� :�? 9Y? ;: � �<? >W*? �*? B?
� *? >W+? :+緸 5� �F? >W�6
? �� +�
2? >W� �H? >W?��
+尽? �J? >W��� ? L? P� W��? V� �? Y:��? _� ��? `� �?���? _� ��? `� ��? � � ? ? �
? !
� � �
� � � � � " � ' � 0 � 7 � A � J � S � [ � f � m � v � ~ � ? � ? � ? � ? ! ? $ ? & ? ' ? ) ? * ? ' ? ( ? ) ? * ? + � p � ? a b ? c d � � ? e b � � ? f b �
? g b �
? h i � � ? j k � " ? l m � 7 ? n o � S b p q ? � r s
t Q � m
� %� u� %� %� %� Q� � *� 0� 9 ? ���? �? � �� %� u� %� %� %� Q� �� v x y � � � � � � ? � � @� ? %Y��zSY��|SY��~SY��€SY��係Y��凷Y���哠Y���圫Y���奡L�孧,+? 嶹? �
� � 1 6 2 9 3 ? > � � @ ? d 6
c d � 9 � ? b � ? � � � � � ? � R � � ��暩 桲? ?? 1 �
� � @ � A
B � � � � ? ? ? � � � ? ? � ? � ?
decompile resulte
package com.glueframework.complier;import java.sql.Connection;import java.sql.DatabaseMetaData;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class InitTable {
public static Boolean crateTable(String tableName, String[] items) throws SQLException {
String urlString = "jdbc:h2:D:/springboot/h2/test";
String usernameString = "admin";
String password = "";
stmt = null;
connection = null;
try {
connection = DriverManager.getConnection(urlString, usernameString, password);
DatabaseMetaData meta = connection.getMetaData();
ResultSet rsTables = meta.getTables(null, null, tableName,
new String[] { "TABLE" });
if (!rsTables.next()) {
stmt = connection.createStatement();
StringBuilder sql = new StringBuilder();
sql.append(" CREATE TABLE ");
if (tableName != null && tableName.length() > 0)
sql.append(tableName);
if (items != null && items.length > 0) {
sql.append(" ( ");
for (int i = 0; i < items.length; i++) {
sql.append(items[i]);
sql.append(" VARCHAR(5000), ");
}
sql.append("PRIMARY KEY(key,environment,groupId,artifactId)) ");
}
stmt.execute(sql.toString());
}
rsTables.close();
return Boolean.valueOf(true);
} finally {
stmt.close();
connection.close();
}
}
public static void main(String[] args) throws SQLException {
String[] items = { "value", "key", "environment", "groupId", "artifactId", "version", "createTime", "updateTime", "flag" };
String tablename = "ConfigerBean";
crateTable(tablename, items);
}
protected static void getClassss() {
forName = Class.forName("com.glueframework.complier.Test");
System.out.println(forName);
}
Statement
Connection
variable?Reproducible example:
blocker.zip
This cannot be decompiled as the process just hangs.
FYI: Luyten can decompile it properly.
output of Procyon:
// 285: invokedynamic BootstrapMethod #0, h:(IJ)Ljava/lang/String;
output of jd-gui:
// 285: h : (IJ)Ljava/lang/String;
1.1.3+, Commit 7f01508
It appears decompiling nested lambdas fails. Instead of emitting the nested lambda the decompiler just prints ()
.
Source:
import java.util.function.Function;
class Test {
void test() {
Function<?, Function<?, ?>> f = i1 -> i2 -> null;
}
}
Decompiled output:
import java.util.function.Function;
class Test {
void test() {
Function function = paramObject -> ();
}
}
This link
https://raw.githubusercontent.com/java-decompiler/mvn-repo/master
is not woking, please fix this
I noticed version 1.0.9 and 1.0.8 are not available on jcenter repository.
Could you publish those?
1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232, javac 11.0.5
It appears for loops over int
s are converted to loops over byte
s even when the values go out of the value range of byte
.
Source:
class ForLoopByte {
void test() {
for (int i = 0; i < 1000; i++) {
Integer j = i;
}
}
}
Decompiled output:
class ForLoopByte {
void test() {
for (byte b = 0; b < '?'; b++)
Integer integer = Integer.valueOf(b);
}
}
I see 40 issues, 5 PR, and commits stopped 2 years ago. Is there any active maintainer for this repo?
If not, is there any good java decompiler in active development? (I have seen cfr, but it does not seem production-ready yet).
In most cases, autoboxing expressions can be decompiled correctly by jd-core.
But I've noticed that there is a flaw when decompiling the following code:
Double.valueOf(0.0).intValue();
It will be decompiled as 0.0.intValue();
.
Obviously, it is incorrect, since basic types cannot be dereferenced.
Is there any way to fix it?
I think it could be transformed to a explicit cast (int) 0.0;
.
Is it possible?
The FSF recommends using either GPL-3.0-only, or GPL-3.0-or-later.
See: https://www.gnu.org/licenses/identify-licenses-clearly.html
The issue was originally raised in the jd-cmd wrapper repo: intoolswetrust/jd-cli#36
1.1.3+, Commit 7f01508
When decompiling a class which uses boxing (e.g. Integer
<-> int
), currently the boxing calls are always emitted.
However, in some cases these boxing calls are required (or the equivalent cast has to be emitted), e.g. when overloaded methods are called.
Source:
class Test {
void test() {
Integer intObj = 10;
int i = intObj;
}
}
Decompiled output:
class Test {
void test() {
Integer integer = Integer.valueOf(10);
int i = integer.intValue();
}
}
This code is not decompiled properly. It is an example code that was provided by you.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jd.core.v1.ClassFileToJavaSourceDecompiler;
import org.jd.core.v1.api.loader.Loader;
import org.jd.core.v1.api.loader.LoaderException;
import org.jd.core.v1.api.printer.Printer;
//$Id$
public class Test {
public static void main(String[] args) throws Exception {
Loader loader = new Loader() {
@Override
public byte[] load(String internalName) throws LoaderException {
InputStream is = this.getClass().getResourceAsStream("/" + internalName + ".class");
if (is == null) {
return null;
} else {
try (InputStream in=is; ByteArrayOutputStream out=new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int read = in.read(buffer);
while (read > 0) {
out.write(buffer, 0, read);
read = in.read(buffer);
}
return out.toByteArray();
} catch (IOException e) {
throw new LoaderException(e);
}
}
}
@Override
public boolean canLoad(String internalName) {
return this.getClass().getResource("/" + internalName + ".class") != null;
}
};
Printer printer = new Printer() {
protected static final String TAB = " ";
protected static final String NEWLINE = "\n";
protected int indentationCount = 0;
protected StringBuilder sb = new StringBuilder();
@Override public String toString() { return sb.toString(); }
@Override public void start(int maxLineNumber, int majorVersion, int minorVersion) {}
@Override public void end() {}
@Override public void printText(String text) {
sb.append(text);
}
@Override public void printNumericConstant(String constant) {
sb.append(constant); }
@Override public void printStringConstant(String constant, String ownerInternalName) {
sb.append(constant); }
@Override public void printKeyword(String keyword) {
sb.append(keyword); }
@Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) {
sb.append(name); }
@Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) {
sb.append(name);
}
@Override public void indent() { this.indentationCount++; }
@Override public void unindent() { this.indentationCount--; }
@Override public void startLine(int lineNumber) {
for (int i=0; i<indentationCount; i++)
sb.append(TAB);
}
@Override public void endLine() {
sb.append(NEWLINE);
}
@Override public void extraLine(int count) { while (count-- > 0) sb.append(NEWLINE); }
@Override public void startMarker(int type) {}
@Override public void endMarker(int type) {}
};
ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler();
Map<String,Object> conf = new ConcurrentHashMap<String, Object>();
decompiler.decompile(loader, printer, "Test$1",conf);
String source = printer.toString();
System.out.println(source);
}
}
Result after decompiling Test$2 is
import java.io.IOException;
import java.io.InputStream;
import org.jd.core.v1.api.loader.Loader;
import org.jd.core.v1.api.loader.LoaderException;
class null implements Loader {
public byte[] load(String internalName) throws LoaderException {
is = getClass().getResourceAsStream("/" + internalName + ".class");
if (is == null)
return null;
try {
throwable1 = null;
throwable2 = null;
try {
} finally {
throwable2 = null;
if (throwable1 == null) {
throwable1 = throwable2;
} else if (throwable1 != throwable2) {
throwable1.addSuppressed(throwable2);
}
}
} catch (IOException e) {
throw new LoaderException(e);
}
}
public boolean canLoad(String internalName) { return (getClass().getResource("/" + internalName + ".class") != null); }
}
Kotlin classes have a @Metadata
annotation, and if k=1
, they generally contain a string table of field names. Kotlin's compiler prefers to use this metadata, rather than the "standard" class name metadata, and it may be unobfuscated even when the standard metadata is obfuscated.
In effect, an "obfuscated" binary may not actually be obfuscated at all, just using a different metadata format.
Unfortunately, this format is not exactly documented, so a compile-flag (or startup presence check) for linking against kotlinx-jvm-metadata is probably needed (the official solution for reading kotlin's metadata)
See title.
Demo Kotlin code is:
object Constants {
@JvmField
val VERY_IMPORTANT = "hello world :)"
val ALSO_IMPORTANT = 42
@JvmStatic
val LAST_IMPORTANT = 69
}
There is a private Constants()
declaration in the class file, as far as I'm aware (both the linter and javac yell at me if I try to directly construct an instance of it, and reflection without setAccessible
fails with an IllegalAccessException
), but it's just not displayed.
(yes this is JD-GUI, but I highly doubt a GUI frontend would be causing this. Unless it's doing some dodgy filtering.)
When 2 enums switch cases exist, one switch case may take the values of another enum.
public enum ColourEnum {
RED, GREEN, BLUE
}
public enum FruitEnum {
BANANA, APPLE, KIWI
}
public class ColourObject {
public ColourEnum getType() {
return ColourEnum.BLUE;
}
}
public class FruitObject {
public FruitEnum getType() {
return FruitEnum.KIWI;
}
}
public class TestEnum {
public static void main(String[] args) {
ColourObject colourObject = new ColourObject();
print(colourObject);
FruitObject fruitObject = new FruitObject();
print(fruitObject);
}
private static void print(FruitObject fruitObject) {
switch (fruitObject.getType()) {
case APPLE:
System.out.println("Apple");
break;
case BANANA:
System.out.println("Banana");
break;
case KIWI:
System.out.println("Kiwi");
break;
default:
System.out.println("Default (fruit)");
break;
}
}
private static void print(ColourObject colourObject) {
switch (colourObject.getType()) {
case GREEN:
System.out.println("Green");
break;
case BLUE:
System.out.println("Blue");
break;
case RED:
System.out.println("Red");
break;
default:
System.out.println("Default (colour)");
break;
}
}
}
The decompilation result does not compile, as the fruits were mistaken for colours
public class TestEnum {
public static void main(String[] args) {
ColourObject colourObject = new ColourObject();
print(colourObject);
FruitObject fruitObject = new FruitObject();
print(fruitObject);
}
private static void print(FruitObject fruitObject) {
switch (fruitObject.getType()) {
case GREEN:
System.out.println("Apple");
return;
case BLUE:
System.out.println("Banana");
return;
case RED:
System.out.println("Kiwi");
return;
}
System.out.println("Default (fruit)");
}
private static void print(ColourObject colourObject) {
switch (colourObject.getType()) {
case GREEN:
System.out.println("Green");
return;
case BLUE:
System.out.println("Blue");
return;
case RED:
System.out.println("Red");
return;
}
System.out.println("Default (colour)");
}
}
It is possible to write (int i) -> new Object[i]
as Object[]::new
, however jd-core currently emits the lambda variant which will always be longer.
1.1.3+, Commit 7f01508
Compiler: javac 11.0.5
Parameter and local var names can collide with type names causing invalid code.
Source:
class TypeHiding {
class paramBoolean { }
class bool { }
void test() {
boolean b = true;
new bool();
}
void test(boolean b) {
new paramBoolean();
}
}
Decompiled output:
class TypeHiding {
class paramBoolean {}
class bool {}
void test() {
boolean bool = true;
new bool();
}
void test(boolean paramBoolean) {
new paramBoolean();
}
}
1.1.3+, Commit 7f01508
It appears access to shadowed super methods is incorrectly decompiled to access to this
(works correctly for fields).
Source:
public class SuperMemberAccess {
private String a;
private String test() {
return null;
}
public static class Child extends SuperMemberAccess {
private int a;
private int test() {
return 1;
}
String doSomething() {
String s = super.test();
return super.a;
}
}
}
Decompiled output:
class SuperMemberAccess {
private String a;
private String test() {
return null;
}
public static class Child extends SuperMemberAccess {
private int a;
private int test() {
return 1;
}
String doSomething() {
// Should call super.test()
String str = test();
return super.a;
}
}
}
1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232
It appears that if a try-with-resources contains nested loops with labels and the try-with-resources statement is left conditionally, then jd-core creates a try-with-resources statement and additionally manually closes the resource, which is invalid and causes malformed code because the referenced exception does not exist as local variable.
Source:
class TryWithResourcesTest implements AutoCloseable {
public void close() { }
static TryWithResourcesTest getInstance() {
return new TryWithResourcesTest();
}
void test() {
try (TryWithResourcesTest obj = getInstance()) {
Label:
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 10; y++) {
if (y > 5) {
continue Label;
}
}
if (x > 20) {
return;
}
}
return;
}
}
}
Decompiled output:
class TryWithResourcesTest implements AutoCloseable {
public void close() {}
static TryWithResourcesTest getInstance() {
return new TryWithResourcesTest();
}
void test() {
// Resource being called "null" is https://github.com/java-decompiler/jd-core/issues/24
try (TryWithResourcesTest null = getInstance()) {
byte b;
label45: for (b = 0; b < 10; b++) {
for (byte b1 = 0; b1 < 10; b1++) {
if (b1 > 5)
continue label45;
}
if (b > 20) {
// jd-core tries to emit code for the "finally" block here as if no try-with-resources
// statement had been created; however because it has been created the local
// variable containing the caught exception does not exist and is therefore called `null`
if (tryWithResourcesTest != null)
if (null != null) {
try {
tryWithResourcesTest.close();
} catch (Throwable throwable) {
null.addSuppressed(throwable);
}
} else {
tryWithResourcesTest.close();
}
return;
}
}
return;
}
}
}
I'm using JD-GUI for macOS version 1.6.6.
While attempting to create a reproducible test for another issue, I ran into this.
I created a simple Test.java, compiled it, and executed it successfully. However, when I tried to open it with the app, nothing appears to have happened.
Here's the source code for the Java 1.8 compiled output that I'm trying to decompile:
public class Test {
public static void main (String args[]) {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
long z = l(x, y);
System.err.println(String.format("x=%d y=%d z=%d", x, y, z));
}
public static long l (int x, int y) {
long rc = ((long)y << 32L) | x;
return rc;
}
}
$ javac Test.java
$ java Test 1 2
x=1 y=2 z=8589934593
I don't anticipate this code causing a core problem but I am wondering if the app will only read jar files. If that's the case, then the open file dialog should prevent me from doing something that is not allowed.
Alternatively, if it is an app problem, it probably would be a good idea for someone to easily find out what to type from the command line to bypass the app.
I have read sample example on home page. I can get string source information from a class files and I want to know is there an existing API can generate corresponding java source file.
1.1.3+, Commit 7f01508
When decompiling a varargs method call currently always a new array creation is emitted, even though this is not always necessary.
However, in some cases the array creation is required, e.g. when overloaded methods are called.
Source:
class Test {
void varargs(int... i) { }
void test() {
varargs(1, 2, 3);
}
}
Decompiled output:
class Test {
void varargs(int... i) { }
void test() {
varargs(new int[] { 1, 2, 3 });
}
}
When try to decompile the below code and it's been wrongly decompiled.
Input Java Class:
public class TestClass {
public int test(){
before();
try {
inTry();
if(condition()) {
return 0;
}
inTryA();
inTryB();
} finally {
inFinally();
}
after();
return 1;
}
private boolean condition() {return false;};
private void before() {};
private void inTry() {};
private void inTryA() {};
private void inTryB() {};
private void inFinally() {};
private void after() {};
}
Decompiled Code:
public class TestClass {
public int test() {
before();
try {
inTry();
if (condition())
return 0;
inTryA();
} finally {
inFinally();
}
inFinally();
after();
return 1;
}
private boolean condition() {return false;};
private void before() {};
private void inTry() {};
private void inTryA() {};
private void inTryB() {};
private void inFinally() {};
private void after() {};
}
JD-Core version: 1.1.3
Compiled Java Version: 1.8.0_252
1.1.3+, Commit 7f01508
When calling the default implementation of an implemented interface the access must occur using TypeName.super.methodName()
, simply using super.methodName()
is invalid:
If the form is
super
.[TypeArguments]Identifier [...] It is a compile-time error if T is the classObject
or T is an interface.
However, jd-core does not emit the type name.
Source:
interface SuperMemberAccess {
interface Base {
default void test() { }
}
public static class A implements Base {
@Override
public void test() {
// Must use TypeName.super.methodName()
Base.super.test();
}
}
}
Decompiled output:
interface SuperMemberAccess {
public static interface Base {
default void test() {}
}
public static class A implements Base {
public void test() {
// This is not valid and will not compile
super.test();
}
}
}
Using jd-core version 1.1.3 from Maven, I decompiled the following class:
public enum EnumWithStaticMethod {
ONE, TWO, THREE;
public static EnumWithStaticMethod valueOf(int i) {
switch(i) {
case 1: return ONE;
case 2: return TWO;
case 3: return THREE;
default: return null;
}
}
}
The decompiled version is:
public enum EnumWithStaticMethod {
ONE, TWO, THREE;
}
I expected the static method to be included in the decompiled source, but it was not.
Would it be possible to have ClassFileToJavaSourceDecompiler.decompile
return the names of additionally decompiled classes (i.e. nested, local and anonymous classes)?
This would allow the caller to determine whether they still have to decompile a class or if it has already been decompiled and printed as nested class.
Deducing this from the class name by checking if it contains a $
is not always reliably possible. An obfuscator might not use this syntax when creating obfuscated names. Additionally $
is a valid (but discouraged) first character of a class name. For example the Gson library uses it for $Gson$Preconditions.java
and $Gson$Types.java
(both top level classes).
Hi,
Just something I noticed was done better in Luyten:
JD Gui got this result:
public static a adliuazd(IBinder param1IBinder) {
// Byte code:
// 0: bipush #18
// 2: bipush #32
// 4: iadd
// 5: bipush #32
// 7: irem
// 8: ifgt -> 14
// 11: goto -> 52
// 14: aload_0
// 15: ifnonnull -> 20
// 18: aconst_null
// 19: areturn
// 20: aload_0
// 21: ldc 'android.support.v4.app.INotificationSideChannel'
// 23: invokestatic qskduaozie : (Landroid/os/IBinder;Ljava/lang/String;)Landroid/os/IInterface;
// 26: astore_1
// 27: aload_1
// 28: ifnull -> 43
// 31: aload_1
// 32: instanceof a/a/a/a/a
// 35: ifeq -> 43
// 38: aload_1
// 39: checkcast a/a/a/a/a
// 42: areturn
// 43: new a/a/a/a/a$a$a
// 46: dup
// 47: aload_0
// 48: invokespecial <init> : (Landroid/os/IBinder;)V
// 51: areturn
// 52: goto -> 14
}
While Luyten got this result:
final IInterface qskduaozie = qskduaozie(binder, "android.support.v4.app.INotificationSideChannel");
if (qskduaozie != null && qskduaozie instanceof a) {
return (a)qskduaozie;
}
return new a(binder);
Maybe something to improve on?
1.1.3+, Commit 7f01508
javac 11.0.5
When an overloading method with array parameter exists and an overload with parameter type Object
is called with an array, the required cast is not added.
Source:
class ArrayMethodOverloading {
void use(Object[] o) { }
void use(Object o) { }
void test() {
use((Object) new Object[] {""});
}
}
Decompiled output:
class ArrayMethodOverloading {
void use(Object[] paramArrayOfObject) {}
void use(Object paramObject) {}
void test() {
use(new Object[] { "" });
}
}
I assume "emmanue1" with the Gmail email address is you @emmanue1. Is there a specific reason why you use a different email address for your commits? This makes it more difficult to identify who made the commits. And I assume this way you will also not get emails when someone comments on one of your commits.
Did you know that you can set an email address per Git project? This GitHub help article describes how to do that. You could then set the GitHub no-reply address.
1.1.3+, Commit 7f01508
Compiler: javac 11.0.5 (javac 8 seems to work as expected)
It appears try-with-resources is decompiled incorrectly resulting in code which could call close()
twice if it throws an exception.
Source:
class TryWithResourcesTest implements AutoCloseable {
public void close() { }
static int test() {
try (TryWithResourcesTest obj = new TryWithResourcesTest()) {
return 1;
}
}
}
Decompiled output:
class TryWithResourcesTest implements AutoCloseable {
public void close() {}
static int test() {
TryWithResourcesTest tryWithResourcesTest = new TryWithResourcesTest();
try {
boolean bool = true;
tryWithResourcesTest.close();
return bool;
} catch (Throwable throwable) {
// This is incorrect, if close() threw the exception this would call close() a second time
try {
tryWithResourcesTest.close();
} catch (Throwable throwable1) {
throwable.addSuppressed(throwable1);
}
throw throwable;
}
}
}
1.1.3+, Commit 7f01508
It appears access to non immediate shadowed super fields is decomiled to super.fieldName
while it should be ((TypeName) this).fieldName
, see JLS 15.11.2 (note that this does not apply for methods, see 15.12.4.4-2.).
Source code:
interface SuperMemberAccess {
static class A {
int i = 1;
}
static class B extends A {
int i = 2;
}
public static class C extends B {
int i = 3;
public void test() {
System.out.println(((A) this).i);
}
}
public static void main(String... args) {
new C().test();
}
}
Decompiled output:
interface SuperMemberAccess {
public static class A {
int i = 1;
}
public static class B extends A {
int i = 2;
}
public static class C extends B {
int i = 3;
public void test() {
// This is incorrect
System.out.println(super.i);
}
}
static void main(String... paramVarArgs) {
(new C()).test();
}
}
It would be great to have a simple tool for use on the command line. Then I could use it directly from Vim.
Using jd-core version 1.1.3 from Maven, I decompiled the following class:
import java.io.InputStream;
public class FilterInputStream extends java.io.FilterInputStream {
protected FilterInputStream(InputStream in) {
super(in);
}
}
I got the result:
import java.io.FilterInputStream;
import java.io.InputStream;
public class FilterInputStream extends FilterInputStream {
protected FilterInputStream(InputStream in) {
super(in);
}
}
The decompiled version does not compile because it uses "extends FilterInputStream" instead of "extends java.io.FilterInputStream".
I'm using JD-GUI 1.6.6.
I compile the following code with Java 1.8.
public static long l (int x, int y) {
long rc = ( (long)y << 32L) | x;
return rc;
}
But when I view the decompiled output, I see this:
public static long l(int x, int y) {
long rc = y << 32L | x;
return rc;
}
Given: x=1, y=2
When I execute the compiled source, I get a return code of 8589934593.
When I compile and execute the source from the decompiled class file, I get a return code of 3.
Using jd-core version 1.1.3 from Maven, I decompiled the following class:
package com.example;
public enum Number {
ONE(1), TWO(2), ONE_HUNDRED(100, "one hundo");
private final int value;
private final String alternateStringRep;
private Number(int value) {
this(value, null);
}
private Number(int value, String alternateStringRep) {
this.value = value;
this.alternateStringRep = alternateStringRep;
}
}
I got the following result:
package com.example;
public enum Number {
ONE(1),
TWO(2),
ONE_HUNDRED(100, "one hundo");
private final String alternateStringRep;
private final int value;
Number(int value, String alternateStringRep) {
this.value = value;
this.alternateStringRep = alternateStringRep;
}
}
The decompiled version is missing the constructor that takes only an int.
1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232
It appears when compiling a try-with-resources without keeping var names (calling javac
without -g:vars
) the resource will be named null
, or rather Printer.printText(String)
is called with null
. But only for the resource declaration, subsequent uses in the try-with-resources statement use the correct name.
Source:
class TryWithResourcesTest implements AutoCloseable {
public void close() { }
static float test() {
try (TryWithResourcesTest obj = new TryWithResourcesTest()) {
return 1;
}
}
}
Decompiled output:
class TryWithResourcesTest implements AutoCloseable {
public void close() {}
static float test() {
// Resource is named null
try (TryWithResourcesTest null = new TryWithResourcesTest()) {
return 1.0F;
}
}
}
import sys
class Crypt:
def init(self,key):
self.key= key
def encrypt(self, msg):
key= list(self.key)
msgList= list(msg)
msgLen=len(msgList)
cipherText = ['']*msgLen
for i in range(msgLen):
cipherText[i]= chr(ord(msgList[i])+int(key))
return ''.join(cipherText)
def decrypt(self,msg):
key= list(self.key)
msgList= list(msg)
msgLen=len(msgList)
cipherText = ['']*msgLen
for i in range(msgLen):
cipherText[i]= chr(ord(msgList[i])+int(key))
return ''.join(cipherText)
if name == "main":
print(sys.argv)
if len(sys.argv)>2:
if sys.argv[1]=='-e':
c= Crypt(sys.argv[2])
print(c.encrypt(sys.argv[3]))
elif sys.argv[1]=='-d':
c = Crypt(sys.argv[2])
print(c.decrypt(sys.argv[3]))
The shared goal of this issue is adding some limited support for decompiling obfuscated binary.
Here is some context:
My personal goal is to complete an assignment where I am supposed to write code to please a simulator. The simulator is obfuscated to prevent us from copying code there. I highly suspect there is a bug in the simulator so that I can't get it to work all the time.
The obfuscated jar I was trying to decompile is here.
Since the binary is obfuscated, in general, I do not expect to recover good names. My personal goal was simply to patch it to debug some issues, as long as the decompiler produce code that could be compiled again, I am happy. (e.g. I can fix the random seed rather easily, I don't need any names)
Here is some findings:
for
or if
as field names), and thus failing compilation.Whenever that happens (a fairly easy to detect situation), we could assign them some other names, like for_0
, the assignment needs to be consistent.
Here is my progress:
As I hack, I tried to just patch the output and see what happens.
For example, filtering the name here:
and here:
That seems to reduce the number of compilation errors, that said, this is totally a hack, I get it.
Next steps:
I am wondering if handling obfuscated binary is an interesting goal for the project. If so, we could collaborate and build some support in it. Tracking down the construction of the Token
object and allows a provided/automatic mapping for field/method names seems like the way to go.
Consider the following enum:
public enum Test {
CLASS_RUNNABLE( new SomeRunnable() ),
FIELD_RUNNABLE( SomeRunnable.INSTANCE ),
METHOD_RUNNABLE( SomeRunnable.getInstance() ),
LAMBDA_RUNNABLE( () -> System.out.println( "Hello" ) ),
MREF_RUNNABLE( SomeRunnable::runInstance );
public static Runnable runnable;
Test( Runnable runnable ) {
this.runnable = runnable;
}
}
Compiling this with javac
and decompiling this with jd-core
produces incorrect java code:
public enum Test {
CLASS_RUNNABLE(new SomeRunnable()),
FIELD_RUNNABLE(SomeRunnable.INSTANCE),
METHOD_RUNNABLE(SomeRunnable.getInstance()),
LAMBDA_RUNNABLE(SomeRunnable.getInstance()),
MREF_RUNNABLE(SomeRunnable::runInstance);
static {
LAMBDA_RUNNABLE = new Test("LAMBDA_RUNNABLE", 3, () -> System.out.println("Hello"));
}
public static Runnable runnable;
Test(Runnable runnable) {
this.runnable = runnable;
}
}
This code is semantically incorrect since enum constructors can't be used directly and enum constants (and static final
constants in general) can't be assigned twice.
Hello,
I tried to use the dj-core in my program and I faced the follow issue:
I used the example from README file and when I call
decompiler.decompile(loader, printer, "C:\Path\to\myclass.class");
I get a NullPointerException.
I would like to know if this is happening because of me or it is something else.
the output is
I use java 8 in windows environment and the latest version of jd-core
java.lang.NullPointerException at org.jd.core.v1.service.converter.classfiletojavasyntax.processor.ConvertClassFileProcessor.process(ConvertClassFileProcessor.java:48) at org.jd.core.v1.service.converter.classfiletojavasyntax.ClassFileToJavaSyntaxProcessor.process(ClassFileToJavaSyntaxProcessor.java:39) at org.jd.core.v1.ClassFileToJavaSourceDecompiler.decompile(ClassFileToJavaSourceDecompiler.java:54) at org.jd.core.v1.ClassFileToJavaSourceDecompiler.decompile(ClassFileToJavaSourceDecompiler.java:38)
Kind regards
Petra
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.