GithubHelp home page GithubHelp logo

jd-core's People

Contributors

emmanue1 avatar marcono1234 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jd-core's Issues

Local var type is chosen without checking if it matches further usage

Version

1.1.3+, Commit 7f01508
Compiler: javac 11.0.5

Description

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);
    }
}

Line Number Alignment

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(){}

Fully Qualified Class Names

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();

For loop converted to using byte calls wrong methods

Version

1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232, javac 11.0.5

Description

It appears for loops over ints are converted to loops over bytes 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); 
    }
}

Decompilation Failure Version 1.0.6

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);
}
  1. user code your demo
  2. Why is there a lack of Statement Connection variable?
  3. my user JD-ui is ok. but your code.

Decompiling nested lambdas fails

Version

1.1.3+, Commit 7f01508

Description

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 -> ();
    }
}

For loop over int is converted to malformed loop over byte

Version

1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232, javac 11.0.5

Description

It appears for loops over ints are converted to loops over bytes 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); 
    }
}

Is this project active yet?

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).

Autoboxing expression decompiled incorrectly

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?

Not needed boxing method calls are emitted

Version

1.1.3+, Commit 7f01508

Description

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();
    }
}

Try with resource statement is not decompiled properly

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); }
}

Use Kotlin metadata for names when available

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)

Missing private constructor on Kotlin object classes

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.

DiscordCanary_N2qgxATJo9
(yes this is JD-GUI, but I highly doubt a GUI frontend would be causing this. Unless it's doing some dodgy filtering.)

Enum switch

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)");
  }
}

Parameter and var names can collide with type names

Version

1.1.3+, Commit 7f01508
Compiler: javac 11.0.5

Description

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();
    }
}

Access to shadowed super methods is incorrectly decompiled to access to `this`

Version

1.1.3+, Commit 7f01508

Description

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;
        }
    }
}

Java 8: try-with-resources containing loop with label uses try-with-resources and closes resource manually

Version

1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232

Description

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;
        } 
    }
}

app won't open a .class file

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.

Not needed array creation for varargs call

Version

1.1.3+, Commit 7f01508

Description

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 });
    }
}

Inappropriate finally block is removed from try statement

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

Access to super default method is missing type name

Version

1.1.3+, Commit 7f01508

Description

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 class Object or T is an interface.

Source: JLS 15.12.1

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();
        }
    }
}

Missing public static method from enum

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.

Feature request: Have decompile method return names of additionally decompiled classes

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).

Byte code got decompiled properly by Luyten

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?

Cast is not added when overloading method with array parameter exists

Version

1.1.3+, Commit 7f01508

Compiler

javac 11.0.5

Description

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[] { "" });
    }
}

Committer email address

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.

Java 11: try-with-resources is decompiled incorrectly

Version

1.1.3+, Commit 7f01508
Compiler: javac 11.0.5 (javac 8 seems to work as expected)

Description

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;
        } 
    }
}

Access to non immediate shadowed fields is incorrect

Version

1.1.3+, Commit 7f01508

Description

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();
    }
}

Decompiled class does not use fully-qualified name of superclass when it collides with decompiled class name

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".

core is producing the wrong code by not including a cast

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.

Missing overloaded enum constructors

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.

Java 8: try-with-resources resource is named null when compiling without keeping var names

Version

1.1.3+, Commit 7f01508
Compiler: javac 1.8.0_232

Description

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;
        } 
    }
}

XOR加密演算法

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]))

Help with decompiling obfuscated binary

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:

  • The decompilation succeed and produced a collection of java files.
  • The generated files used keywords (like 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:

printer.printReference(token.getType(), token.getInternalTypeName(), token.getName(), token.getDescriptor(), token.getOwnerInternalName());

and here:

printer.printDeclaration(token.getType(), token.getInternalTypeName(), token.getName(), token.getDescriptor());

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.

Enum constants with lambdas are initialized in static initializer block

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.

Programmatically use of decompiler Error

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.