Coder Social home page Coder Social logo

mstrobel / procyon Goto Github PK

View Code? Open in Web Editor NEW
948.0 948.0 116.0 33.04 MB

Procyon is a suite of Java metaprogramming tools, including a rich reflection API, a LINQ-inspired expression tree API for runtime code generation, and a Java decompiler.

License: Other

Java 99.79% Jasmin 0.21%

procyon's People

Contributors

codingfabian avatar davknav avatar georgeto avatar gpicron avatar marcono1234 avatar mstrobel avatar mstrobel-strike avatar nbauma109 avatar vmpn 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

procyon's Issues

StackOverflowError occurred unexpected

Well, when I decompress the jar file "spring-tx-5.2.9.RELEASE" into a directory, and I traverse the classes in the directory and call the method Decompiler.decompile(String internalName, ITextOutput output, DecompilerSettings settings),this leads to a StackOverflowError, It seems that the functionCall of the method "recurse" in InsertNecessaryConversionsTransform led to the problem.I was really comfused what should I do to solve it.

com.strobel.expressions.ExpressionVisitor.visitLambda has too restrictive return type

The ExpressionVisitor function

protected <T> LambdaExpression<T> visitLambda(final LambdaExpression<T> node) {

has a return type that is too restrictive (a lambda can be visited and replaced only with a lambda having the same functional interface).

Because of this, using the ExpressionVisitor to re-write the return type of a lambda is not possible without hijacking the visit method with a type check and a custom method call, e.g.

public static class MyExpressionVisitor extends ExpressionVisitor
{
    private <T> Expression visitAnyLambda(final LambdaExpression<T> node)
    {
        return visitLambda(node);
    }

    @Override
    public Expression visit(final Expression node)
    {
        return node instanceof LambdaExpression<?> ? visitAnyLambda((LambdaExpression<?>) node) : super.visit(node);
    }
}

I think there is merit in changing the visitLambda function signature as proposed above (to return an Expression instead of LambdaExpression<T>), to improve ExpressionVisitor re-usability.

As far as I could see, changing this signature all over the code does not produre failures in unit tests, it only requires to change the corresponding Optimizer function

static <T> LambdaExpression<T> optimize(final LambdaExpression<T> node) {

and eventually the classes overriding it (DebugViewWriter, ExpressionStringBuilder and VariableBinder), even if it is not strictly required due to covariancy.

Option to decode unicode string as it is

Right now unicode stirngs would be decompiled to \u{hex} like \u7efc\u5408.

I believe it would be great if there exists an option to decode those Unicode strings to their original UTF-8 character.

Invocation of static generic method of generic class causes "Illegal type in constant pool"

Trying to invoke the static generic method

Map.<K,V>of(K k1, V v1, K k2, V v2)

produces an "Illegal type in constant pool" error during the lambda class generation.

Below the test case I am using:

    @Test
    void canInvokeGenericStaticMethodOfGenericClass() {
        final var interfaceType = Type.of(Supplier.class).makeGenericType(Types.Map.makeGenericType(Types.String, Types.Integer));
        // Map.of(K k1, V v1, K k2, V v2)
        final var mapOfMethod =
            Types.Map.getMethod("of", Types.Object, Types.Object, Types.Object, Types.Object).makeGenericMethod(Types.String, Types.Integer);
        final var invoke =
            Expression.call(
                mapOfMethod,
                new ExpressionList<>(Expression.constant("k1"), Expression.constant(22), Expression.constant("k2"), Expression.constant(109))
        );
        final var lambda = Expression.lambda(interfaceType, invoke);
        //noinspection unchecked
        final var delegate = (Supplier<Map<String, Integer>>) lambda.compile();
        final var map = delegate.get();
        assertNotNull(map);
        assertEquals(map.get("key2"), 109);
    }

Attached the generated class (zipped).
lambda_mapOf.zip

Incorrect line numbering for lambda expressions

Hi,

We noticed that the line numbering produced by using the --debug-line-numbers flag are incorrect for lambda expressions, consider the following example:

Original code:

import java.util.ArrayList;

public class Main {
    public static void main(String[] argv) {
        ArrayList<String> test = new ArrayList<>();
        test.add("a");
        test.add("b");
        test.add("c");
        test.forEach(str -> {



            System.out.println(str);



        });
    }
}

Decompiled code:

{
    public static void main(final String[] array) {
        final ArrayList list = /*EL:5*/new ArrayList();
        /*SL:6*/list.add("a");
        /*SL:7*/list.add("b");
        /*SL:8*/list.add("c");
        /*SL:9*/list.forEach(x -> System.out.println(x));
    }
}

As can be seen the decompiled code shows that the call to System.out.println happens in line 9, while in fact it happened in line 13.

We have an idea why this happening but we are not sure, our analysis shows that the parsing of the LineNumberTable structures from the class files bytecode refers to the wrapping function (in this case main) and not the lambda expression which leads to the incorrect line numbering report.

Please let us know your thoughts on this, and whether we could expect a fix for this issue.

Thanks.

Can't decompile anymore - IllegalStateException

As the title line says!

Using: Java SE 8u311

java.lang.IllegalStateException: Invalid BootstrapMethods attribute entry: 2 additional arguments required for method java/lang/invoke/StringConcatFactory.makeConcatWithConstants, but only 1 specified.

try with resources decompilation is not accurate

I was suggested to open this java decompiler issue from here
deathmarine/Luyten#290

A jdk8 class file with "try with resources" code doesnt decompile correctly
Here's the source
image

And here's what the tool outputs ( im using v0.5.4) in my default config

image

I couldn't find past issues around this area.
Is this addressed in any of the recent builds?
thanks in advance!

ClassCastException when reflecting a type that has generic constructors

Source seems to be this code here: https://github.com/mstrobel/procyon/blob/master/Procyon.Reflection/src/main/java/com/strobel/reflection/Resolver.java#L331-L350

It assumes that the only sources of generic declarations are 1) methods and 2) classes, but misses 3) constructors.

Minimal reproduction case:

static class Foo {
	public int bar;
	
	public <T> Foo(List<T> b) { }
}

public static void main(final String[] args) {
	Type.of(Foo.class).getField("bar");
}

ๆ–นๆณ•็š„ๅฝขๅ‚ๅง‹็ปˆไผš่ขซ่ฎพ็ฝฎไธบfinal

com.strobel.decompiler.languages.java.ast.AstBuilder#createParameters
image
่ฟ™้‡Œๆฒกๆœ‰่ฎพ็ฝฎmodifyใ€‚
com.strobel.decompiler.languages.java.ast.transforms.DeclareVariablesTransform#run(com.strobel.decompiler.languages.java.ast.AstNode, com.strobel.decompiler.languages.java.ast.DefiniteAssignmentAnalysis)
image
ๆญคๅค„ๆฒกๆœ‰่ฎพ็ฝฎmodify๏ผŒ็„ถๅŽๅˆ่ขซ่ฎพ็ฝฎไบ†finalใ€‚ๆ— ่ฎบ่ฎพ็ฝฎmodify่ฟ˜ๆ˜ฏๆฒกๆœ‰่ฎพ็ฝฎmodify้ƒฝไผšๅ˜ๆˆfinalไฟฎ้ฅฐใ€‚ๆญคๅค„ไธๅคŸ่‡ชๅฎšไน‰ใ€‚

Nested catch statements that span multiple basic blocks lead to invalid decompilations/exceptions

The following classes (manually assembled by Krakatau) fail to decompile with the develop branch of Procyon.

.version 49 0
.class super public NestedCatchA
.super java/lang/Object

.method static public a : (Ljava/lang/Object;)V
    .limit stack 3
    .limit locals 2

    L1: 
        aload_0
        ifnonnull L3
        aconst_null
        athrow
        goto L3
    L2: 
        .stack stack_1 Object java/lang/Throwable
        athrow
        .stack same
    L3: 
        getstatic java/lang/System out Ljava/io/PrintStream;
        ldc "A"
        invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
        .stack same
        return
    L4: 
        .stack stack_1 Object java/lang/Throwable
        astore_1
        getstatic java/lang/System out Ljava/io/PrintStream;
        ldc "B"
        invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
        .stack same
        return

    .catch java/lang/Throwable from L1 to L2 using L2
    .catch java/lang/Throwable from L2 to L3 using L4
.end method

.method static public main : ([Ljava/lang/String;)V
    .limit stack 2
    .limit locals 2
    ldc "foo"
    invokestatic NestedCatchA a (Ljava/lang/Object;)V
    aconst_null
    invokestatic NestedCatchA a (Ljava/lang/Object;)V
    return
.end method
.end class
.version 49 0
.class super public NestedCatchB
.super java/lang/Object

.method static public a : (Ljava/lang/Object;)V
    .limit stack 3
    .limit locals 2

    L1: 
        aload_0
        ifnonnull L3
        aconst_null
        athrow
    L3: 
        getstatic java/lang/System out Ljava/io/PrintStream;
        ldc "A"
        invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
        .stack same
        return
    L2: 
        .stack stack_1 Object java/lang/Throwable
        athrow
        .stack same
    L4: 
        astore_1
        getstatic java/lang/System out Ljava/io/PrintStream;
        ldc "B"
        invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
        .stack same
        return

    .catch java/lang/Throwable from L1 to L3 using L2
    .catch java/lang/Throwable from L3 to L2 using L4
.end method

.method static public main : ([Ljava/lang/String;)V
    .limit stack 2
    .limit locals 2
    ldc "foo"
    invokestatic NestedCatchB a (Ljava/lang/Object;)V
    aconst_null
    invokestatic NestedCatchB a (Ljava/lang/Object;)V
    return
.end method
.end class

The former (NestedCatchA), decompiles to source code that javac rejects due to throw; not providing an expression.

// 
// Decompiled by Procyon v1.0-SNAPSHOT
// 

public class NestedCatchA
{
    public static void a(final Object o) {
        Label_0010: {
            try {
                if (o == null) {
                    throw null;
                }
                break Label_0010;
            }
            catch (Throwable t) {
                throw t;
            }
            try {
                throw;
                System.out.println("A");
            }
            catch (Throwable t2) {
                System.out.println("B");
            }
        }
    }

    public static void main(final String[] array) {
        a("foo");
        a(null);
    }
}

The latter (NestedCatchB) causes the exception from com.strobel.decompiler.ast.Error.expressionLinkedFromMultipleLocations to be thrown.

// 
// Decompiled by Procyon v1.0-SNAPSHOT
// 

public class NestedCatchB
{
    public static void a(final Object p0) {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     1: ifnonnull       6
        //     4: aconst_null    
        //     5: athrow         
        //     6: getstatic       java/lang/System.out:Ljava/io/PrintStream;
        //     9: ldc             "A"
        //    11: invokevirtual   java/io/PrintStream.println:(Ljava/lang/String;)V
        //    14: return         
        //    15: athrow         
        //    16: astore_1       
        //    17: getstatic       java/lang/System.out:Ljava/io/PrintStream;
        //    20: ldc             "B"
        //    22: invokevirtual   java/io/PrintStream.println:(Ljava/lang/String;)V
        //    25: return         
        //    StackMapTable: 00 04 0E 40 07 00 0B 00 08
        //    Exceptions:
        //  Try           Handler
        //  Start  End    Start  End    Type                 
        //  -----  -----  -----  -----  ---------------------
        //  0      6      15     16     Ljava/lang/Throwable;
        //  6      15     16     26     Ljava/lang/Throwable;
        // 
        // The error that occurred was:
        // 
        // java.lang.IllegalStateException: Expression is linked from several locations: Label_0006:
        //     at com.strobel.decompiler.ast.Error.expressionLinkedFromMultipleLocations(Error.java:27)
        //     at com.strobel.decompiler.ast.AstOptimizer.mergeDisparateObjectInitializations(AstOptimizer.java:2596)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:235)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:214)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:99)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:782)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:675)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:552)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:519)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:161)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:150)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:125)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
        //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:330)
        //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:144)
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }

    public static void main(final String[] array) {
        a("foo");
        a(null);
    }
}

I'd expect them to decompile to something similar to NestedCatch{A,B}Expected.

public class NestedCatchAExpected {
    public static void a(final Object o) {
        try {
            if(o == null) {
                throw null;
            }
            System.out.println("A");
        }
        catch (Throwable t) {
            try {
                throw t;
            } catch(Throwable t2) {
                System.out.println("B");
            }
        }
    }

    public static void main(String[] args) {
        a("foo");
        a(null);
    }
}
public class NestedCatchBExpected {
    public static void a(final Object o) {
        try {
            if(o == null) {
                throw null;
            }
            try {
                System.out.println("A");
            } catch(Throwable t2) {
                System.out.println("B");
            }
        }
        catch (Throwable t) {
            throw t;
        }
    }

    public static void main(String[] args) {
        a("foo");
        a(null);
    }
}

I'd be interested in working on a fix, if you have suggestions for how to proceed.

Procyon 'eats' first instruction.

I did not look into the sources, but I think that procyon assmues that the first instruction is a label or something like that.

        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     1: invokespecial   java/lang/Object.<init>:()V
        //     4: return         
        // 
        // The error that occurred was:
        // 
        // java.lang.ArrayIndexOutOfBoundsException: 0
        //     at com.strobel.decompiler.ast.AstBuilder.performStackAnalysis(AstBuilder.java:1940)
        //     at com.strobel.decompiler.ast.AstBuilder.build(AstBuilder.java:108)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:211)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:99)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:782)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createConstructor(AstBuilder.java:713)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:549)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:519)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:161)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:150)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:125)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)

That hpapens only with non-static methods.

Incorrect Enum handling in CodeGenerator

The CodeGenerator tries to identify an Enum field using toString.
This can fail if the toString method of the field has been customized (e.g. ChronoUnit).

I suppose that the following code

if (unboxedType.isEnum()) {
getField(unboxedType.getField(value.toString()));
return true;

should be changed to

if (unboxedType.isEnum()) {
	getField(unboxedType.getField(((Enum<?>)value).name()));
	return true;
}

considering that name() is the value expected by Enum.valueOf too.

Int decompiled to bool

Hello, I am running some tests in Java bytecode (using Jasmin) and attempting to decompile the resulting class files using Procyon. In some cases Procyon incorrectly decompiles an int as a boolean, resulting in a switch on a boolean. This is technically recompilable (if โ€“enable-preview is used during compilation), but it looks unintentional - what is causing this?

Thanks!

Version info

Procyon decompiler 0.6.0
OpenJDK 19.0.2 2023-01-17
x86_64 Ubuntu 22.04

Example

Example bytecode file, which I compile to a class file (ClassFile.zip):

.class public TestCase
.super java/lang/Object

; default constructor
.method public <init>()V
	aload_0
	invokespecial java/lang/Object/<init>()V
	return
.end method

.method public static main([Ljava/lang/String;)V
	.limit stack 5
	.limit locals 6

block_0:

    bipush 1

	; switch
    lookupswitch
    	0: block_2
    	default : block_2

block_2:
  return

Decompiling the class file using Procyon gives this:

//
// Decompiled by Procyon v0.6.0
//

public class TestCase
{
	public static void main(final String[] array) {
    	switch (true) {
        	default: {}
    	}
	}
}

com.strobel.reflection.Type.containsGenericParameter returns incorrect result for generic types

The function

public boolean containsGenericParameter(final Type<?> genericParameter) {

returns an incorrect result for generic types, expecially if compared to
public boolean containsGenericParameters() {

Specifically, containsGenericParameter does not take into account closed generics


just generic type definitions, while containsGenericParameters performs the check on both.

Removing the line


fixes the inconsistency.

NullPointerException decompiling org/tanukisoftware/wrapper/WrapperProcessConfig

Decompiling org/tanukisoftware/wrapper/WrapperProcessConfig...
java.lang.NullPointerException
at com.strobel.decompiler.languages.java.ast.transforms.RewriteSwitchExpressionsTransform.visitSwitchStatement(RewriteSwitchExpressionsTransform.java:238)
at com.strobel.decompiler.languages.java.ast.transforms.RewriteSwitchExpressionsTransform.visitSwitchStatement(RewriteSwitchExpressionsTransform.java:26)
at com.strobel.decompiler.languages.java.ast.SwitchStatement.acceptVisitor(SwitchStatement.java:66)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitBlockStatement(DepthFirstAstVisitor.java:104)
at com.strobel.decompiler.languages.java.ast.BlockStatement.acceptVisitor(BlockStatement.java:72)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitMethodDeclaration(DepthFirstAstVisitor.java:229)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclarationOverride(ContextTrackingVisitor.java:81)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclaration(ContextTrackingVisitor.java:73)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclaration(ContextTrackingVisitor.java:28)
at com.strobel.decompiler.languages.java.ast.MethodDeclaration.acceptVisitor(MethodDeclaration.java:94)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitTypeDeclaration(DepthFirstAstVisitor.java:259)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclarationOverride(ContextTrackingVisitor.java:66)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclaration(ContextTrackingVisitor.java:57)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclaration(ContextTrackingVisitor.java:28)
at com.strobel.decompiler.languages.java.ast.TypeDeclaration.acceptVisitor(TypeDeclaration.java:90)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitCompilationUnit(DepthFirstAstVisitor.java:269)
at com.strobel.decompiler.languages.java.ast.CompilationUnit.acceptVisitor(CompilationUnit.java:82)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.run(ContextTrackingVisitor.java:97)
at com.strobel.decompiler.languages.java.ast.transforms.RewriteSwitchExpressionsTransform.run(RewriteSwitchExpressionsTransform.java:65)
at com.strobel.decompiler.languages.java.ast.transforms.TransformationPipeline.runTransformationsUntil(TransformationPipeline.java:98)
at com.strobel.decompiler.languages.java.ast.AstBuilder.runTransformations(AstBuilder.java:120)
at com.strobel.decompiler.languages.java.JavaLanguage.runTransforms(JavaLanguage.java:97)
at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:72)
at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
wrapper.zip

"java.lang.ClassCastException: class com.strobel.assembler.metadata.CoreMetadataFactory$MethodSignature cannot be cast to class com.strobel.assembler.metadata.MethodReference"

I ran into this when decompiling Minecraft 1.16.5, aqm.class.

Stacktrace

java.lang.ClassCastException: class com.strobel.assembler.metadata.CoreMetadataFactory$MethodSignature cannot be cast to class com.strobel.assembler.metadata.MethodReference (com.strobel.assembler.metadata.CoreMetadataFactory$MethodSignature and com.strobel.assembler.metadata.MethodReference are in unnamed module of loader 'app')
	at com.strobel.decompiler.languages.java.utilities.TypeUtilities.getExpectedTypeByParent(TypeUtilities.java:365)
	at com.strobel.decompiler.languages.java.utilities.RedundantCastUtility$IsRedundantVisitor.visitCastExpression(RedundantCastUtility.java:344)
	at com.strobel.decompiler.languages.java.utilities.RedundantCastUtility$IsRedundantVisitor.visitCastExpression(RedundantCastUtility.java:167)
	at com.strobel.decompiler.languages.java.ast.CastExpression.acceptVisitor(CastExpression.java:55)
	at com.strobel.decompiler.languages.java.utilities.RedundantCastUtility.isCastRedundant(RedundantCastUtility.java:67)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitCastExpression(InsertNecessaryConversionsTransform.java:80)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitCastExpression(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.CastExpression.acceptVisitor(CastExpression.java:55)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitCastExpression(DepthFirstAstVisitor.java:279)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitCastExpression(InsertNecessaryConversionsTransform.java:59)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitCastExpression(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.CastExpression.acceptVisitor(CastExpression.java:55)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitReturnStatement(DepthFirstAstVisitor.java:149)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitReturnStatement(InsertNecessaryConversionsTransform.java:179)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitReturnStatement(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.ReturnStatement.acceptVisitor(ReturnStatement.java:57)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.recurse(InsertNecessaryConversionsTransform.java:576)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.addCastForAssignment(InsertNecessaryConversionsTransform.java:336)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitReturnStatement(InsertNecessaryConversionsTransform.java:222)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitReturnStatement(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.ReturnStatement.acceptVisitor(ReturnStatement.java:57)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.recurse(InsertNecessaryConversionsTransform.java:576)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.addCastForAssignment(InsertNecessaryConversionsTransform.java:336)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitReturnStatement(InsertNecessaryConversionsTransform.java:222)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitReturnStatement(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.ReturnStatement.acceptVisitor(ReturnStatement.java:57)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitBlockStatement(DepthFirstAstVisitor.java:104)
	at com.strobel.decompiler.languages.java.ast.BlockStatement.acceptVisitor(BlockStatement.java:72)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitLambdaExpression(DepthFirstAstVisitor.java:384)
	at com.strobel.decompiler.languages.java.ast.LambdaExpression.acceptVisitor(LambdaExpression.java:49)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitInvocationExpression(DepthFirstAstVisitor.java:59)
	at com.strobel.decompiler.languages.java.ast.InvocationExpression.acceptVisitor(InvocationExpression.java:78)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitAssignmentExpression(DepthFirstAstVisitor.java:329)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitAssignmentExpression(InsertNecessaryConversionsTransform.java:159)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitAssignmentExpression(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.AssignmentExpression.acceptVisitor(AssignmentExpression.java:88)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitExpressionStatement(DepthFirstAstVisitor.java:109)
	at com.strobel.decompiler.languages.java.ast.ExpressionStatement.acceptVisitor(ExpressionStatement.java:47)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitBlockStatement(DepthFirstAstVisitor.java:104)
	at com.strobel.decompiler.languages.java.ast.BlockStatement.acceptVisitor(BlockStatement.java:72)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitIfElseStatement(DepthFirstAstVisitor.java:134)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitIfElseStatement(InsertNecessaryConversionsTransform.java:487)
	at com.strobel.decompiler.languages.java.ast.transforms.InsertNecessaryConversionsTransform.visitIfElseStatement(InsertNecessaryConversionsTransform.java:37)
	at com.strobel.decompiler.languages.java.ast.IfElseStatement.acceptVisitor(IfElseStatement.java:83)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitBlockStatement(DepthFirstAstVisitor.java:104)
	at com.strobel.decompiler.languages.java.ast.BlockStatement.acceptVisitor(BlockStatement.java:72)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitLambdaExpression(DepthFirstAstVisitor.java:384)
	at com.strobel.decompiler.languages.java.ast.LambdaExpression.acceptVisitor(LambdaExpression.java:49)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitInvocationExpression(DepthFirstAstVisitor.java:59)
	at com.strobel.decompiler.languages.java.ast.InvocationExpression.acceptVisitor(InvocationExpression.java:78)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitExpressionStatement(DepthFirstAstVisitor.java:109)
	at com.strobel.decompiler.languages.java.ast.ExpressionStatement.acceptVisitor(ExpressionStatement.java:47)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitBlockStatement(DepthFirstAstVisitor.java:104)
	at com.strobel.decompiler.languages.java.ast.BlockStatement.acceptVisitor(BlockStatement.java:72)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitMethodDeclaration(DepthFirstAstVisitor.java:214)
	at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclaration(ContextTrackingVisitor.java:64)
	at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclaration(ContextTrackingVisitor.java:28)
	at com.strobel.decompiler.languages.java.ast.MethodDeclaration.acceptVisitor(MethodDeclaration.java:85)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitTypeDeclaration(DepthFirstAstVisitor.java:244)
	at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclaration(ContextTrackingVisitor.java:52)
	at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclaration(ContextTrackingVisitor.java:28)
	at com.strobel.decompiler.languages.java.ast.TypeDeclaration.acceptVisitor(TypeDeclaration.java:90)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
	at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitCompilationUnit(DepthFirstAstVisitor.java:249)
	at com.strobel.decompiler.languages.java.ast.CompilationUnit.acceptVisitor(CompilationUnit.java:81)
	at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.run(ContextTrackingVisitor.java:84)
	at com.strobel.decompiler.languages.java.ast.transforms.TransformationPipeline.runTransformationsUntil(TransformationPipeline.java:93)
	at com.strobel.decompiler.languages.java.ast.AstBuilder.runTransformations(AstBuilder.java:119)
	at com.strobel.decompiler.languages.java.JavaLanguage.runTransforms(JavaLanguage.java:97)
	at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:72)
	at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
	at us.deathmarine.luyten.DecompilerLinkProvider.generateContent(DecompilerLinkProvider.java:97)
	at us.deathmarine.luyten.OpenFile.decompileWithNavigationLinks(OpenFile.java:494)
	at us.deathmarine.luyten.OpenFile.decompile(OpenFile.java:467)
	at us.deathmarine.luyten.Model.extractClassToTextPane(Model.java:420)
	at us.deathmarine.luyten.Model.openEntryByTreePath(Model.java:339)
	at us.deathmarine.luyten.Model$TreeListener$1.run(Model.java:266)

Case sensetive class name

There is problem decompiling whole JAR to directory in windows when JAR contains different classes with name different only by case.
For example aa.class and Aa.class
The only solution is send files directly to ZIP file (if we want generate -source,jar artifact for example)

com.strobel.reflection.TypeCache can store incorrect association between Object[] and array of generic parameters

The TypeCache class generates the same descriptor for both Object[] and T[], causing incorrect association of a descriptor to a type, depending on which type populates the cache first.

Below code provides a test example

package com.strobel.reflection;

import org.junit.Test;

import java.util.Arrays;

import static org.junit.Assert.assertEquals;

public class CustomTests {
    @Test
    public void genericParameterArrayTypesDoNotCollideInTypeCache() {
        final Type<MyClass> c1 = Type.of(MyClass.class); // Load class with method having T[] parameter
        c1.getMethods();  // Load methods of class with method having T[] parameter
        final Type<MyOtherClass> c2 = Type.of(MyOtherClass.class); // Load class with method having Object[] parameter
        final Type<Object[]> objectArrayType = Types.Object.makeArrayType(); // Prepare Object[] type for comparison
        final MethodInfo method = c2.getMethod("getFirstOrNull", objectArrayType); // Load specific method having Object[] parameter
        final Type<?> methodParameterType = method.getParameters().getParameterTypes().get(0); // Retrieve method parameter type (expected to be Object[])
        assertEquals(objectArrayType, methodParameterType); // The method parameter type should match the expected Object[], but will be T[]
    }

    private static class MyClass {
        public static <T> Iterable<T> enumerate(final T[] items) {
            return Arrays.asList(items);
        }
    }

    private static class MyOtherClass {
        public static Object getFirstOrNull(final Object... values) {
            return values.length > 0 ? values[0] : null;
        }
    }
}

Above code fails, unsless c2 method is loaded before c1 methods.

The cause is that

this.descriptor = type.getInternalName();

calls the getInternalName of an ArrayType, which returns
return "[" + _elementType.getErasedSignature();

which is [Ljava/lang/Object; for both Object[] and T[].

If the T[] is loaded first, it will then populate the cache _definitionMap, as per


storing T[] as the equivalent type of [Ljava/lang/Object;. Once updated, it will not change anymore.

Due to this, the getFirstOrNull method above will incorrectly return that the array type used as parameter is T[], instead of Object[]

If I did not misunderstood the purpose of the TypeCache._map, it wants to store the most recent instance of a type, matching the descriptor and generic type arguments.
If so, in my opinion the descriptor could be changed like this:

            this.descriptor =
                type.isArray() && type.getElementType().isGenericParameter() ? "[L" + type.getElementType().getInternalName() + ";" : type.getInternalName();

But I am not really sure this is the expected behavior... could you explain a bit the _map purpose, expecially regarding generic types (both bounded and unbounded)?

MethodInfo hashCode issue

The current implementation of MethodInfo.hashCode returns the same value for overloaded methods.

As an example, below code

var abs1 = Type.of(Math.class).getMethod("abs", Types.Integer);
var abs2 = Type.of(Math.class).getMethod("abs", Types.Long);
String.format("%s vs %s:\nAre equal? %s\nHave same hashCode? %s", abs1, abs2, abs1.equals(abs2), abs1.hashCode() == abs2.hashCode());

will print

public static int abs(int) vs public static long abs(long):
Are equal? false
Have same hashCode? true

This means that all hash-based collections will never store overloaded methods together, only the last added one.

I noticed that some MemberInfo-derived classes override isEquivalentTo, without touching hashCode.
I suppose that all classes overriding IsEquivalentTo, should override hashCode accordingly.

Validation of method-based coercion expression is not correct

The check to ensure the method singature of a method-based UnaryExpression expression is probably not correct.

The current method is

private static UnaryExpression getMethodBasedCoercionOperator(
final ExpressionType unaryType,
final Expression operand,
final Type convertToType,
final MethodInfo method) {
assert method != null;
validateOperator(method);
final ParameterList parameters = method.getParameters();
if (parameters.size() != 0) {
throw Error.incorrectNumberOfMethodCallArguments(method);
}
final Type returnType = method.getReturnType();
if (TypeUtils.areReferenceAssignable(convertToType, returnType)) {
return new UnaryExpression(unaryType, operand, returnType, method);
}
// check for auto(un)boxing call
if (TypeUtils.isAutoUnboxed(convertToType) && returnType.isPrimitive() &&
TypeUtils.areEquivalent(returnType, TypeUtils.getUnderlyingPrimitive(convertToType))) {
return new UnaryExpression(unaryType, operand, convertToType, method);
}
throw Error.methodBasedOperatorMustHaveValidReturnType(unaryType, method);
}

which means that the coercion method must be:

  • A valid operator (see below)
  • Parameterless
  • A method whose return type is allowed to be assigned to the expected coercion type

To be a valid operator, as per the following function

private static void validateOperator(final MethodInfo method) {
assert method != null;
if (method.isStatic()) {
throw Error.operatorMethodMustNotBeStatic(method);
}
final Type returnType = method.getReturnType();
if (returnType == PrimitiveTypes.Void) {
throw Error.operatorMethodMustNotReturnVoid(method);
}
final ParameterList parameters = method.getParameters();
if (parameters.size() != 0) {
throw Error.operatorMethodParametersMustMatchReturnValue(method);
}
final Type<?> returnClass = TypeUtils.getBoxedTypeOrSelf(returnType);
if (!TypeUtils.areReferenceAssignable(method.getDeclaringType(), returnClass)) {
throw Error.methodBasedOperatorMustHaveValidReturnType(method);
}
}

it must be:

  • Non-Static
  • Non-Void returning
  • Parameterless
  • Declared by a class compatible with its return type

Above criteria do not meet what I would expect from a coercion operator, because there is no way for a non-static parameterless method declared by the expected return type, to convert a value from another type (that cannot be supplied). Eventually, it should be provided by a type assignable from the operand type.

Looking at the 'default' coercion methods

public static MethodInfo getCoercionMethod(final Type<?> source, final Type<?> destination) {
// NOTE: If destination type is an autoboxing type, we will need an implicit box later.
final Type<?> unboxedDestinationType = isAutoUnboxed(destination)
? getUnderlyingPrimitive(destination)
: destination;
if (destination == Types.String) {
return Types.String.getMethod("valueOf", source);
}
/*
if (!destination.isPrimitive()) {
return null;
}
*/
final MethodInfo method;
if (destination == PrimitiveTypes.Integer) {
method = source.getMethod("intValue", BindingFlags.PublicInstance);
}

we can see that the actual criteria could be
either:

  • Static
  • Provided by a type assignable from the coercion type
  • Accepting a parameter compatible with the provided operand type
  • Returning a type compatible with the coercion method
    like String::valueOf
    or
  • Non-Static
  • Provided by a type assignable from the operand type
  • Parameterless
  • Returning a type compatible with the coercion method
    like Long.intValue().

At least, the default coercion methods seem to fall in the above categories (and to be honest, I would avoid the strict check over the declaring type: why a static method provided by another class to perform the conversion is not allowed?).

For reference the current .NET implementation requires the method to be:

  • Static
  • Accepting a single parameter assignable from the operand type
  • Able to return a value assignable from the expected coercion type

IndexOutOfBoundsExceptions

When decompiling particular classes, I sometimes encounter IndexOutOfBoundsExceptions, which manifest in the decompiled code as shown below:

//           
// This method could not be decompiled.          
//           
// Original Bytecode:          
//           
//  [...]      
//           
// The error that occurred was:          
//           
// java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 0          
//     at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)          
//     at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)          
//     at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
//     [...]

I tried to look into this myself, but I had to realize, that I just understand too little of the decompilation process to find the root cause.
This problem is present since I started using your marvelous decompiler back in 2018 and also applies to the most recent v0.6.0 release. Due to the fact, that I used the decompiler primarily on licensed legacy code, I never found a way to provide some reproduction steps here.

Well, up to now.
By accident I found another class, that shows the same problem, but is freely available - compiled and in source.
(BTW: There is surely no need to decompile kotlin-stdlib, but as it shows the exact same exception as on the legacy java code, I use it for reproduction here.)

So, please consider the following reproduction steps:

  1. Download Kotlin-Stdlib (https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.20/kotlin-stdlib-1.7.20.jar)
  2. Perform decompile on this JAR (my command was java -jar procyon-decompiler-0.6.0.jar -ln -sl -o /tmp/out kotlin-stdlib-1.7.20.jar
  3. Open /tmp/out/kotlin/SynchronizedLazyImpl.java and find the inline comments of the decompiler logging the java.lang.IndexOutOfBoundsException.

Some observations:

  • There seem to be several inline exceptions in other classes, too, but with other causes. I don't want to overcomplicate things in this issue, so I'm fine with sticking to the IndexOutOfBoundsException for now.
  • While most of the exceptions happen silently (only within the inline-comments of decompiled classes), one IndexOutOfBoundsException is actually thrown in the console (after Decompiling kotlin/text/StringsKt__StringsKt...). But this one seems unrelated to the one in SynchronizedLazyImpl.java.

If you require more details (like the full inline comment), let me know. I didn't want to blow up the description here.

Please support Java 21

I was able to compile procyon with Java 21 using release target 8, but have encountered some test failures:

  • SwitchTests.testEnumSwitch - enum switch was decompiled as an ordinal switch
  • ThirdPartyTests.testUnboxToNumber - decompiled code contains Constable, not Number.
  • ThirdPartyTests.testLiteralAssignments - test specifies float numbers with excess precision, Java truncates those since 19.
  • ThirdPartyTests.testOptimizedVariables, testSwitchKrakatau, testWhileLoopsKrakatau - decompiled variable does not match ('b' vs 'x')

Incorrect byte code generated for nested lambda with closure

I have encountered an issue compiling a lambda using a closure in a nested lambda.

This is the expression I am trying to generate and compile (the getOrNull method, included below, is basically a null-conditional get accessor):
(Ctx ctx) -> getOrNull(ctx, x -> getOrNull(ctx.items.get("key_2"), k -> k.value))

This is the a test to replicate the issue:

public class CustomTests {
    public static <T, R> R getOrNull(final T instance, @NotNull final Func1<T, R> getter) {
        if (instance == null) {
            return null;
        }
        return getter.apply(instance);
    }

    @Test
    public void nestedLambdaWithClosureCompilation() {
        final Type<Ctx> ctxType = Type.of(Ctx.class);
        final Type<Ctx.Item> itemType = Type.of(Ctx.Item.class);
        final MethodInfo getOrNullMethod =
            Type.of(CustomTests.class).getMethod("getOrNull", BindingFlags.PublicStatic, Types.Object, Type.of(Func1.class));

        // Reference expression: (Ctx ctx) -> getOrNull(ctx, x -> getOrNull(ctx.items.get("key_2"), k -> k.value))
        // 1. Create parameter [(Ctx ctx)]
        final ParameterExpression ctxParameter = Expression.parameter(ctxType, "ctx");

        // 2. Create value lambda getter [(Ctx.Item k) -> k.value]
        final ParameterExpression kParameter = Expression.parameter(itemType, "k");
        final MemberExpression itemGetterBody = Expression.field(kParameter, itemType.getField("value"));
        final Type<Func1<Ctx.Item, Object>> itemGetterDelegateType = Type.of(Func1.class).makeGenericType(itemType, Types.Object);
        final LambdaExpression<Func1<Ctx.Item, Object>> itemGetter = Expression.lambda(itemGetterDelegateType, itemGetterBody, kParameter);

        // 3. Create item accessor [ctx.items.get("key_2")]
        final MemberExpression itemsAccessor = Expression.field(ctxParameter, ctxType.getField("items"));
        final MethodCallExpression getValueByKey =
            Expression.call(itemsAccessor, itemsAccessor.getType().getMethod("get", Types.String), Expression.constant("key_2"));

        // 4. Create conditional getter [getOrNull(ctx.items.get("key_2"), k -> k.value)]
        final MethodCallExpression getValueConditional =
            Expression.call(getOrNullMethod.makeGenericMethod(getValueByKey.getType(), Types.Object), getValueByKey, itemGetter);

        // 5. Create conditional getter as lambda getter [(Ctx x) -> getOrNull(ctx.items.get("key_2"), k -> k.value)] (A closure is used instead of the parameter)
        final ParameterExpression xParameter = Expression.parameter(ctxType, "x");
        final Type<Func1<Ctx, Object>> getValueConditionalDelegateType = Type.of(Func1.class).makeGenericType(ctxType, Types.Object);
        final LambdaExpression<Func1<Ctx, Object>> getValueConditionalLambda =
            Expression.lambda(getValueConditionalDelegateType, getValueConditional, xParameter);

        // 6. Create lambda [getOrNull(ctx, x -> getOrNull(ctx.items.get("key_2"), k -> k.value))]
        final MethodCallExpression body = Expression.call(getOrNullMethod.makeGenericMethod(ctxType, Types.Object), ctxParameter, getValueConditionalLambda);
        final LambdaExpression<Func1<Ctx, Object>> lambda =
            Expression.lambda(Type.of(Func1.class).<Func1<Ctx, Object>>makeGenericType(ctxType, Types.Object), body, ctxParameter);

        final Func1<Ctx, Object> delegate = lambda.compile();
        assertNotNull(delegate);
    }

    private static class Ctx {
        public Map<String, Item> items = new LinkedHashMap<>();
        public Double number = 23.3;

        public Ctx() {
            items.put("key_2", new Item());
        }

        public static class Item {
            public Integer value = 55;
        }
    }
}

The compiled byte code fails verification phase (VerifyError).
I used ASM to have a better validation error, and analyzed the attached f__Lambda$0x0002.class and it seems that the generated bytecode passes the Closure as a parameter to the actual invoke method, while it should be passed to the constructor of the delegate (if I am not wrong).

Decompiling class files from Java bytecode source - switch issue

Hello, I am running some tests in Java bytecode (using Jasmin) and attempting to decompile the resulting class files using Procyon. I have run into a problem whereby Procyon inserts unreachable break statements within switch statements. I understand that this is not the typical use case for Procyon but it would be interesting to know whether the decompilation failure is due to the bytecode switch statements being constructed in a way that is out-of-scope for Procyon, or something else.

Thanks!

Version info

Procyon decompiler 0.6.0
OpenJDK 19.0.2 2023-01-17
x86_64 Ubuntu 22.04

Example

A simple example bytecode file is below, which I compile to a class file (here: TestCase.zip).

.class public TestCase
.super java/lang/Object

; default constructor
.method public <init>()V
	aload_0
	invokespecial java/lang/Object/<init>()V
	return
.end method

.method public static main([Ljava/lang/String;)V
	.limit stack 5
	.limit locals 6

block_0:

    bipush 3

    ; switch
    lookupswitch
    	0: block_2
    	default : block_2

block_2:
   
    bipush 3

   ; switch
    lookupswitch
   	 0: block_3
   	 default: block_3

block_3:
   return
   	 
  	 
.end method

The decompiled Procyon output is this:

//
// Decompiled by Procyon v0.6.0
//

public class TestCase
{
	public static void main(final String[] array) {
    	switch (3) {
        	default: {
            	switch (3) {
                	default: {
                    	return;
                	}
            	}
            	break;
        	}
    	}
	}
}


Recompiling this gives the following error:

TestCase.java:15: error: unreachable statement
            	break;
            	^
1 error

NullPointerException decompiling org/eclipse/jetty/util/MultiException

Decompiling org/eclipse/jetty/util/MultiException...
java.lang.NullPointerException
at com.strobel.decompiler.languages.java.ast.transforms.RewriteSwitchExpressionsTransform.visitSwitchStatement(RewriteSwitchExpressionsTransform.java:238)
at com.strobel.decompiler.languages.java.ast.transforms.RewriteSwitchExpressionsTransform.visitSwitchStatement(RewriteSwitchExpressionsTransform.java:26)
at com.strobel.decompiler.languages.java.ast.SwitchStatement.acceptVisitor(SwitchStatement.java:66)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitBlockStatement(DepthFirstAstVisitor.java:104)
at com.strobel.decompiler.languages.java.ast.BlockStatement.acceptVisitor(BlockStatement.java:72)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitMethodDeclaration(DepthFirstAstVisitor.java:229)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclarationOverride(ContextTrackingVisitor.java:81)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclaration(ContextTrackingVisitor.java:73)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitMethodDeclaration(ContextTrackingVisitor.java:28)
at com.strobel.decompiler.languages.java.ast.MethodDeclaration.acceptVisitor(MethodDeclaration.java:94)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitTypeDeclaration(DepthFirstAstVisitor.java:259)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclarationOverride(ContextTrackingVisitor.java:66)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclaration(ContextTrackingVisitor.java:57)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.visitTypeDeclaration(ContextTrackingVisitor.java:28)
at com.strobel.decompiler.languages.java.ast.TypeDeclaration.acceptVisitor(TypeDeclaration.java:90)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitChildren(DepthFirstAstVisitor.java:41)
at com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor.visitCompilationUnit(DepthFirstAstVisitor.java:269)
at com.strobel.decompiler.languages.java.ast.CompilationUnit.acceptVisitor(CompilationUnit.java:82)
at com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor.run(ContextTrackingVisitor.java:97)
at com.strobel.decompiler.languages.java.ast.transforms.RewriteSwitchExpressionsTransform.run(RewriteSwitchExpressionsTransform.java:65)
at com.strobel.decompiler.languages.java.ast.transforms.TransformationPipeline.runTransformationsUntil(TransformationPipeline.java:98)
at com.strobel.decompiler.languages.java.ast.AstBuilder.runTransformations(AstBuilder.java:120)
at com.strobel.decompiler.languages.java.JavaLanguage.runTransforms(JavaLanguage.java:97)
at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:72)
at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
jetty-util-9.4.33.v20201020.jar.zip

Method decompilation fails with "Unsupported node type: com.strobel.decompiler.ast.Lambda"

Decompilation of the following class fails:

import java.util.function.Function;

public class LambdaTest {

  public Function<String, String> doSomething() {
    return (String s) -> {
      int x = -1;
      while (true) {
        if (x == -2) {
          break;
        }

        if (x == -1) {
          break;
        }
      }
      return s;
    };
  }
}

Tested with openjdk8 and latest git version (fec0d7a)

The error is:

        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     5: areturn        
        //    Signature:
        //  ()Ljava/util/function/Function<Ljava/lang/String;Ljava/lang/String;>;
        // 
        // The error that occurred was:
        // 
        // java.lang.IllegalStateException: Unsupported node type: com.strobel.decompiler.ast.Lambda
        //     at com.strobel.decompiler.ast.Error.unsupportedNode(Error.java:32)
        //     at com.strobel.decompiler.ast.GotoRemoval.exit(GotoRemoval.java:612)
        //     at com.strobel.decompiler.ast.GotoRemoval.exit(GotoRemoval.java:586)
        //     at com.strobel.decompiler.ast.GotoRemoval.transformLeaveStatements(GotoRemoval.java:625)
        //     at com.strobel.decompiler.ast.GotoRemoval.removeGotosCore(GotoRemoval.java:57)
        //     at com.strobel.decompiler.ast.GotoRemoval.removeGotos(GotoRemoval.java:53)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:276)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:208)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:94)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:862)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:755)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:632)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:599)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:196)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:163)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:138)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
        //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
        //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
        //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
        // 

How to decompile a whole directory?

how can i decompile all jars of a directory and get an output with the same directory tree?

e.g. :

main dir:
-X
--blah
--- aaa.jar
--bb.jar
-Y
--blah
---blah
----aaa.jar
--aaa.jar
-Z
--aaa.jar
--aaa.jar

and i want to get the source folder like above dir-tree.

Can procyon rename local variables?

It's generating that code:

public G(final some 0) {
        super();
        this.field = 0;
    }

from that bytecode:

DEFINE PUBLIC <init>(Lsomething/some; 1)V
A:
ALOAD this
INVOKESPECIAL java/lang/RuntimeException.<init>()V
ALOAD this
ALOAD 1
PUTFIELD something/G.field Lsomething/some;
RETURN
B:
C:

but it should change 1 from ALOAD to var1 for example

Where does one get decompiler.jar?

In the Wiki, the "available here" link refers to Bitbucket which downloads a text file that says to look in Github. Where is the decompiler.jar referenced in the Wiki available from? Really don't want to have to spend a week configuring a new environment and build it just to try it out.

Inconsistent behavior for new array of Number with numeric initializers

Numeric types are not handled consistently when trying to initialize arrays.

In general, trying to initialize a Number[] fails with both primitive and reference numeric types, but with different errors.

Below a couple of test to reproduce the issue:

    @Test
    public void newNumberArrayWithPrimitiveNumeric() {
        final Number[] array = new Number[] { 3, (Double) 0.9, (Number) 1.12f };

        final Expression newArray =
            Expression.newArrayInit(
                Types.Number,
                Expression.constant(3, PrimitiveTypes.Integer),
                Expression.constant(0.9, Types.Double),
                Expression.constant(1.12f, Types.Number)
            );
        final LambdaExpression<Supplier<Number[]>> lambda = Expression.lambda(Type.of(Supplier.class).makeGenericType(Types.Number.makeArrayType()), newArray);
        final Supplier<Number[]> delegate = lambda.compile();
        assertEquals(array[2], delegate.get()[2]);
    }

    @Test
    public void newNumberArrayWithoutPrimitiveNumeric() {
        final Number[] array = new Number[] { (Integer) 3, (Double) 0.9, (Number) 1.12f };

        final Expression newArray =
            Expression.newArrayInit(
                Types.Number,
                Expression.constant((Integer)3, Types.Integer),
                Expression.constant(0.9, Types.Double),
                Expression.constant(1.12f, Types.Number)
            );
        final LambdaExpression<Supplier<Number[]>> lambda = Expression.lambda(Type.of(Supplier.class).makeGenericType(Types.Number.makeArrayType()), newArray);
        final Supplier<Number[]> delegate = lambda.compile();
        assertEquals(array[2], delegate.get()[2]);
    }

Attached the full test class.

Notice that both initializations (with or without primitive types) are legal in Java, but in Procyon

  • for primitive types, the Expression throws an exception (i.e. "An expression of type 'int' cannot be used to initialize an array of type 'java.lang.Number'", even if it is a superclass of the boxed value)
  • for reference types, the Expression accepts the values, but an assertion in the CodeGenerator fails (actually, complaining about emitting a constant for the values provided as initializer).

The tests show that using an Object[] results in the same behavior, while assignment between Number/Object and numeric primitives/references works fine.

Cannot find symbol defineClass in TypeBuilder

How are we meant to compile the project?
I tried ./gradlew build on a cloned repository, and it fails with an error on my host:

/tmp/procyon/Procyon.Reflection/src/main/java/com/strobel/reflection/emit/TypeBuilder.java:1234: error: cannot find symbol
            _generatedClass = (Class<T>) getUnsafeInstance().defineClass(
                                                            ^
  symbol:   method defineClass(String,byte[],int,int,ClassLoader,ProtectionDomain)
  location: class Unsafe
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
1 error

I am using OpenJDK 12.0.2.

com.strobel.reflection.Resolver should return concrete generic type for fields

As far as I could see, the current resolve logic for fields does not return a concrete generic type when it should.

As an example, given the following classes

    private static final class TestObject extends GenericTestObject<Double> {
    }

    public static class GenericTestObject<T> {
        public List<T> items;

        public T sum(final List<T> values) {
            throw new UnsupportedOperationException();
        }
    }

executing Type.of(TestObject.class).getField("items") returns java.util.List<T> (generic declaration), instead of java.util.List<java.langDouble>, which is what I expected.

The underlying field (returned by Type.of(TestObject.class).getField("items").getRawField()) holds the correct information through the getGenericType() (which is safe to call even for non-generic fields, returning the field getType()).

Hence, in my opinion, the following line of code


should call field.getGenericType() instead.

Methods behave as expected, invoking the getGeneric... versions for parameters, return type and exception types

java.lang.reflect.Type[] parameterTypes = m.getGenericParameterTypes();

ClassCastException when decompiling complex lambda code

  • Procyon 0.5.36
  • javac 1.8.0_221
    Java file:
import java.util.function.Consumer;
import java.util.stream.Stream;

class ProcyonsBane {
    void lambdaHell() {
        stream().flatMap(string -> {
            Consumer consumer = (foo) -> {
            };
            return stream().map(list -> new EvilClass("" , consumer));
        });
    }

    <T> Stream<T> stream() {
        return null;
    }

    static class EvilClass {
        EvilClass(String id, Consumer consumer) {
        }
    }
}

Compiled classfile:
https://filebin.net/nb6t20n2dmp14n87/ProcyonBane.class?t=erjq2wfm

Error: https://gist.github.com/natanfudge/7b73912ca34a4da69f1e8944a6cef78b

Important note: from what I can tell, this snippet triggers 3 separate Procyon bugs. That is, it is likely after you fix this you will face another crash (stack overflow), and then after fixing that one you will have yet another exception (IllegalArgumentException). Obviously, it has been extracted from a much bigger portion of code, to be the smallest possible example that fails to decompile. This is currently blocking us from using Procyon.

Publish decompiler artifacts

Hello,
could you please publish the decompiler artifacts (.jar, sources, javadoc)?
This would be really useful for projects which want to use it as dependency. Also it would be good if the artifact would then not be a fat jar. IDEs such as Eclipse have problems finding the source for the transitive dependencies in this case and it would prevent excluding or increasing the version number of the transitive dependencies.

Can the decompile() method return String?

I want to decompile class files and store the context into a String so I could further manipulate the context and finally present it to the end user. Is that possible?

NoClassDefFoundError: sun/misc/URLClassPath when running with Java 11

When using Procyon using Java11, the following error is thrown:

java.lang.NoClassDefFoundError: sun/misc/URLClassPath
	at com.strobel.assembler.metadata.ClasspathTypeLoader.<init>(ClasspathTypeLoader.java:66)
	at com.strobel.assembler.metadata.ClasspathTypeLoader.<init>(ClasspathTypeLoader.java:42)
	at com.strobel.assembler.InputTypeLoader.<init>(InputTypeLoader.java:45)
	at org.sf.feeling.decompiler.procyon.decompiler.ProcyonDecompiler.decompile(ProcyonDecompiler.java:75)
	at org.sf.feeling.decompiler.procyon.decompiler.ProcyonDecompiler.decompileFromArchive(ProcyonDecompiler.java:206)
	at org.sf.feeling.decompiler.editor.BaseDecompilerSourceMapper.decompile(BaseDecompilerSourceMapper.java:314)
	at org.sf.feeling.decompiler.editor.BaseDecompilerSourceMapper.findSource(BaseDecompilerSourceMapper.java:168)
	at org.sf.feeling.decompiler.editor.DecompilerSourceMapper.findSource(DecompilerSourceMapper.java:72)
	at org.sf.feeling.decompiler.editor.JavaDecompilerClassFileEditor.doOpenBuffer(JavaDecompilerClassFileEditor.java:173)
	at org.sf.feeling.decompiler.editor.JavaDecompilerClassFileEditor.doOpenBuffer(JavaDecompilerClassFileEditor.java:148)
	at org.sf.feeling.decompiler.editor.JavaDecompilerClassFileEditor.doOpenBuffer(JavaDecompilerClassFileEditor.java:140)
	at org.sf.feeling.decompiler.editor.JavaDecompilerClassFileEditor.doSetInput(JavaDecompilerClassFileEditor.java:472)
	at org.eclipse.ui.texteditor.AbstractTextEditor.lambda$1(AbstractTextEditor.java:3179)
	at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:436)
	at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:352)
	at org.eclipse.ui.internal.WorkbenchWindow.lambda$5(WorkbenchWindow.java:2368)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:72)
	at org.eclipse.ui.internal.WorkbenchWindow.run(WorkbenchWindow.java:2366)
	at org.eclipse.ui.texteditor.AbstractTextEditor.internalInit(AbstractTextEditor.java:3196)
	at org.eclipse.ui.texteditor.AbstractTextEditor.init(AbstractTextEditor.java:3221)
	at org.eclipse.ui.internal.EditorReference.initialize(EditorReference.java:353)
	at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.create(CompatibilityPart.java:340)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
	at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:998)
	at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:963)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalInject(InjectorImpl.java:139)
	at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:408)
	at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:331)
	at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:202)
	at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.createFromBundle(ReflectionContributionFactory.java:91)
	at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.doCreate(ReflectionContributionFactory.java:60)
	at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.create(ReflectionContributionFactory.java:42)
	at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer.createWidget(ContributedPartRenderer.java:132)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createWidget(PartRenderingEngine.java:1002)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:662)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:768)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:739)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:733)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:45)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:717)
	at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer.showTab(StackRenderer.java:1293)
	at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.lambda$0(LazyStackRenderer.java:75)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.lambda$0(UIEventHandler.java:38)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:236)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:146)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:4622)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application$1.syncExec(E4Application.java:219)
	at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:38)
	at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:205)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:203)
	at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:234)
	at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151)
	at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132)
	at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:75)
	at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:44)
	at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55)
	at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:63)
	at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElementGen(ElementContainerImpl.java:170)
	at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:188)
	at org.eclipse.e4.ui.internal.workbench.ModelServiceImpl.showElementInWindow(ModelServiceImpl.java:651)
	at org.eclipse.e4.ui.internal.workbench.ModelServiceImpl.bringToTop(ModelServiceImpl.java:615)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.delegateBringToTop(PartServiceImpl.java:790)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.bringToTop(PartServiceImpl.java:404)
	at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.showPart(PartServiceImpl.java:1239)
	at org.eclipse.ui.internal.WorkbenchPage.busyOpenEditor(WorkbenchPage.java:3205)
	at org.eclipse.ui.internal.WorkbenchPage.lambda$9(WorkbenchPage.java:3110)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:72)
	at org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:3108)
	at org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:3078)
	at org.eclipse.ui.internal.WorkbenchPage.openEditor(WorkbenchPage.java:3069)
	at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor(EditorUtility.java:376)
	at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor_aroundBody0(EditorUtility.java:182)
	at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility$AjcClosure1.run(EditorUtility.java:1)
	at org.jetbrains.kotlin.aspects.navigation.KotlinOpenEditorAspect.ajc$around$org_jetbrains_kotlin_aspects_navigation_KotlinOpenEditorAspect$1$d64491c4proceed(KotlinOpenEditorAspect.aj:16)
	at org.jetbrains.kotlin.aspects.navigation.KotlinOpenEditorAspect.ajc$around$org_jetbrains_kotlin_aspects_navigation_KotlinOpenEditorAspect$1$d64491c4(KotlinOpenEditorAspect.aj:29)
	at org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.openInEditor(EditorUtility.java:167)
	at org.eclipse.jdt.ui.JavaUI.openInEditor(JavaUI.java:737)
	at org.eclipse.jdt.internal.ui.actions.OpenTypeAction.runWithEvent(OpenTypeAction.java:93)
	at org.eclipse.jdt.internal.ui.actions.OpenTypeAction.runWithEvent(OpenTypeAction.java:170)
	at org.eclipse.ui.internal.handlers.ActionDelegateHandlerProxy.execute(ActionDelegateHandlerProxy.java:281)
	at org.eclipse.ui.internal.handlers.E4HandlerProxy.execute(E4HandlerProxy.java:95)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58)
	at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:318)
	at org.eclipse.e4.core.internal.di.InjectorImpl.invoke(InjectorImpl.java:252)
	at org.eclipse.e4.core.contexts.ContextInjectionFactory.invoke(ContextInjectionFactory.java:173)
	at org.eclipse.e4.core.commands.internal.HandlerServiceHandler.execute(HandlerServiceHandler.java:156)
	at org.eclipse.core.commands.Command.executeWithChecks(Command.java:498)
	at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:487)
	at org.eclipse.e4.core.commands.internal.HandlerServiceImpl.executeHandler(HandlerServiceImpl.java:213)
	at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.executeCommand(KeyBindingDispatcher.java:308)
	at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.press(KeyBindingDispatcher.java:584)
	at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.processKeyEvent(KeyBindingDispatcher.java:653)
	at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.filterKeySequenceBindings(KeyBindingDispatcher.java:443)
	at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher.access$2(KeyBindingDispatcher.java:386)
	at org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher$KeyDownFilter.handleEvent(KeyBindingDispatcher.java:96)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
	at org.eclipse.swt.widgets.Display.filterEvent(Display.java:1199)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1056)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1081)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1066)
	at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:1108)
	at org.eclipse.swt.widgets.Text.sendKeyEvent(Text.java:1768)
	at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:1104)
	at org.eclipse.swt.widgets.Widget.wmChar(Widget.java:1491)
	at org.eclipse.swt.widgets.Control.WM_CHAR(Control.java:4879)
	at org.eclipse.swt.widgets.Text.WM_CHAR(Text.java:2636)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:4761)
	at org.eclipse.swt.widgets.Text.windowProc(Text.java:2618)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:4812)
	at org.eclipse.swt.internal.win32.OS.DispatchMessage(Native Method)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3583)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1049)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:155)
	at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:633)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:557)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:150)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:203)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:137)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:107)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:255)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:660)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:597)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1468)

This is encountered while running Procyon in Eclipse 2019-09 R (4.13.0) with ECD (Enhanced Class Decompiler) 3.1.1.

Homebrew integration is broken

Hi @mstrobel.

Since you moved the project from bitbucket to GitHub, the homebrew formula has been disabled. I'm using diffoscope at work to do binary diffs of JARs and, without procyon-decompiler on the path (installed by brew), the results aren't as impressive.

I'm not sure if your bitbucket repo also had tags, but those are missing from GitHub. Updating the formula above to point to release files seems to want to have tags to enable GitHub support.

I also suggest updating the link on your Twitter profile to point to GitHub instead of bitbucket.

MemberExpression should expose getMember() as public

Access to MemberExpression.getMember() is currently package-private

In my opinion, it should be changed to public, to align with the MethodCallExpression.getMethod()

Otherwise, code visiting MemberExpression has to be put in the com.strobel.expressions package to access the relevant MemberInfo.

Apparent hang decompiling coherence-14-1-1-0-7.jar

Decompilation stops at com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray.

11:47:32 [FINE] JarTypeLoader: Attempting to load type: com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray...
11:47:32 [FINE] JarTypeLoader: Type loaded from C:\tools\test\coherence-14.1.1.0.7.jar!com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray.class.
11:47:32 [FINE] AstBuilder: Beginning bytecode AST construction for com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray.remove:(J)Ljava/lang/Object;...
11:47:32 [FINE] AstBuilder: Pruning exception handlers...
11:47:32 [FINE] AstBuilder: Inlining subroutines...
11:47:32 [FINE] AstBuilder: Removing inlined `finally` code...
11:47:32 [FINE] AstBuilder: Performing stack analysis...
11:47:32 [FINE] AstBuilder: Creating bytecode AST...
11:47:32 [FINE] AstBuilder: Finished bytecode AST construction for com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray.remove:(J)Ljava/lang/Object;.
11:47:32 [FINE] AstOptimizer: Beginning bytecode AST optimization...
11:47:32 [FINE] AstOptimizer: Performing optimization: RemoveRedundantCode.
11:47:32 [FINE] AstOptimizer: Performing optimization: ReduceBranchInstructionSet.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineVariables.
11:47:32 [FINE] AstOptimizer: Performing optimization: CopyPropagation.
11:47:32 [FINE] AstOptimizer: Performing optimization: RewriteFinallyBlocks.
11:47:32 [FINE] AstOptimizer: Performing optimization: SplitToMovableBlocks.
11:47:32 [FINE] AstOptimizer: Performing optimization: RemoveUnreachableBlocks.
11:47:32 [FINE] AstOptimizer: Performing optimization: TypeInference.
11:47:32 [FINE] AstOptimizer: Performing block-level bytecode AST optimizations (enable FINER for more detail)...
11:47:32 [FINER] AstOptimizer: Optimizing block #1, round 1...
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: RemoveInnerClassAccessNullChecks.
11:47:32 [FINE] AstOptimizer: Performing optimization: PreProcessShortCircuitAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyShortCircuit.
11:47:32 [FINE] AstOptimizer: Performing optimization: JoinBranchConditions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyTernaryOperator.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: JoinBasicBlocks.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyLogicalNot.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformObjectInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformArrayInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: IntroducePostIncrement.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineConditionalAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MakeAssignmentExpressions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineLambdas.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineVariables2.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MergeDisparateObjectInitializations.
11:47:32 [FINER] AstOptimizer: Optimizing block #1, round 2...
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: RemoveInnerClassAccessNullChecks.
11:47:32 [FINE] AstOptimizer: Performing optimization: PreProcessShortCircuitAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyShortCircuit.
11:47:32 [FINE] AstOptimizer: Performing optimization: JoinBranchConditions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyTernaryOperator.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: JoinBasicBlocks.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyLogicalNot.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformObjectInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformArrayInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: IntroducePostIncrement.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineConditionalAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MakeAssignmentExpressions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineLambdas.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineVariables2.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MergeDisparateObjectInitializations.
11:47:32 [FINER] AstOptimizer: Optimizing block #1, round 3...
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: RemoveInnerClassAccessNullChecks.
11:47:32 [FINE] AstOptimizer: Performing optimization: PreProcessShortCircuitAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyShortCircuit.
11:47:32 [FINE] AstOptimizer: Performing optimization: JoinBranchConditions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyTernaryOperator.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: JoinBasicBlocks.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyLogicalNot.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformObjectInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformArrayInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: IntroducePostIncrement.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineConditionalAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MakeAssignmentExpressions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineLambdas.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineVariables2.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MergeDisparateObjectInitializations.
11:47:32 [FINER] AstOptimizer: Optimizing block #2, round 1...
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: RemoveInnerClassAccessNullChecks.
11:47:32 [FINE] AstOptimizer: Performing optimization: PreProcessShortCircuitAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyShortCircuit.
11:47:32 [FINE] AstOptimizer: Performing optimization: JoinBranchConditions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyTernaryOperator.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: JoinBasicBlocks.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyLogicalNot.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformObjectInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformArrayInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: IntroducePostIncrement.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineConditionalAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MakeAssignmentExpressions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineLambdas.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineVariables2.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MergeDisparateObjectInitializations.
11:47:32 [FINER] AstOptimizer: Optimizing block #3, round 1...
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: RemoveInnerClassAccessNullChecks.
11:47:32 [FINE] AstOptimizer: Performing optimization: PreProcessShortCircuitAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyShortCircuit.
11:47:32 [FINE] AstOptimizer: Performing optimization: JoinBranchConditions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyTernaryOperator.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: JoinBasicBlocks.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: SimplifyLogicalNot.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformObjectInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: TransformArrayInitializers.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: IntroducePostIncrement.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineConditionalAssignments.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MakeAssignmentExpressions.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineLambdas.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: InlineVariables2.
11:47:32 [FINER] AstOptimizer: Performing block-level optimization: MergeDisparateObjectInitializations.
11:47:32 [FINE] AstOptimizer: Performing optimization: FindLoops.
11:47:32 [FINE] AstOptimizer: Performing optimization: FindConditions.
11:47:32 [FINE] AstOptimizer: Performing optimization: FlattenNestedMovableBlocks.
11:47:32 [FINE] AstOptimizer: Performing optimization: RemoveRedundantCode2.
11:47:32 [FINE] AstOptimizer: Performing optimization: GotoRemoval.
11:47:32 [FINE] AstOptimizer: Performing optimization: DuplicateReturns.
11:47:32 [FINE] AstOptimizer: Performing optimization: ReduceIfNesting.
11:47:32 [FINE] AstOptimizer: Performing optimization: GotoRemoval2.
11:47:32 [FINE] AstOptimizer: Performing optimization: ReduceComparisonInstructionSet.
11:47:32 [FINE] AstOptimizer: Performing optimization: RecombineVariables.
11:47:32 [FINE] AstOptimizer: Performing optimization: RemoveRedundantCode3.
11:47:32 [FINE] AstOptimizer: Performing optimization: CleanUpTryBlocks.
11:47:32 [FINE] AstOptimizer: Performing optimization: InlineVariables3.
11:47:32 [FINE] AstOptimizer: Performing optimization: TypeInference2.
11:47:32 [FINE] AstOptimizer: Finished bytecode AST optimization.
11:47:32 [FINE] JarTypeLoader: Attempting to load type: com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray...
11:47:32 [FINE] JarTypeLoader: Type loaded from C:\tools\test\coherence-14.1.1.0.7.jar!com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray.class.
11:47:32 [FINE] AstBuilder: Beginning bytecode AST construction for com/tangosol/coherence/component/util/windowedArray/ConcurrentWindowedArray.removeInternal:(JJZ)Ljava/lang/Object;...
11:47:32 [FINE] AstBuilder: Pruning exception handlers...
11:47:32 [FINE] AstBuilder: Inlining subroutines...
11:47:32 [FINE] AstBuilder: Removing inlined `finally` code...

Questionable performance

Hello!
I have been working on one application that is supposed to deobfuscate java source code to be more specific, to deobfuscate Minecraft mods. Recently I have decided that it would be a nice feature to be able to process jar files directly without the necessity of using external software so I decided to use your decompiler to fulfill this purpose!

The task is simple. All that I need is to turn .class file into .java file (decompile it). So I have created a function that will read a class file and return it as source:

public static final DecompilerSettings DECOMP_SETTINGS = DecompilerSettings.javaDefaults();

private String decompile(String sourcePath)
{
if (!sourcePath.endsWith(".class"))
	return null;

//double t0 = System.nanoTime();

StringWriter sw = new StringWriter();
Decompiler.decompile(sourcePath, new PlainTextOutput(sw), DECOMP_SETTINGS);
String result = sw.toString();

//double t = System.nanoTime();
//System.out.println((t-t0)/1000000);
return result;
}

The returned string is then written back into file but this process is not slow!

However, I soon found a problem, since I need to decompile a lot of classes I need it to be fast!
So I have decided to create a simple benchmark (commented code) and found out that to decompile one simple class approximately 13kB and not even 250 lines of code it needs approximately 1.9 sec! Don't get me wrong! It may sounds like a good score but in fact, that's actually terrible... Especially when I need to decompile 1000 of classes like that or even bigger!

I think there must be something that I am doing wrong, also the fact that this site http://www.javadecompilers.com/ that proclaims to use Procyon as well can decompile pretty big jars almost 10x faster than my implementation which just confirms my thought!

So the question is what am I doing wrong or are there any performance tips or improvements I should consider?
Please help! I would really like to cut that time at last in half!

Thank you!

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.