leibnitz27 / cfr

This is the public repository for the CFR Java decompiler
https://www.benf.org/other/cfr
MIT License
2k stars 258 forks source link

Suggest to add an options to ignore load class when 'JrtPresent' is true #282

Closed tzfun closed 2 years ago

tzfun commented 2 years ago

CFR version

0.152

Description

I use the jdk11 environment to run the cfr program to decompile the classes compiled by jdk8, which will force the loading of the imported classes in the class, and these classes do not exist in the decompilation environment, which makes it impossible to use normally.

I checked the the source code found that if the org.benf.cfr.reader.state.ClassFileSourceImpl#JrtPresent is true, it will try to load imported class, In getContentByFromReflectedClass() method of the outer catch only Exception, my this kind of situation will throw an Error, so did not catch it, is in fact failed to load should also be regarded as normal.

For example, the decompiler program does not have Hibernate, and the class file that needs to be decompiled has Imported Hibernate, it will throw such an exception:

Caused by: java.lang.NoClassDefFoundError: org/hibernate/service/ServiceRegistry
        at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
        at java.base/java.lang.Class.forName(Class.java:315) ~[na:na]
        at org.benf.cfr.reader.state.ClassFileSourceImpl.getContentByFromReflectedClass(ClassFileSourceImpl.java:189) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.ClassFileSourceImpl.getInternalContent(ClassFileSourceImpl.java:237) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.ClassFileSourceImpl.getClassFileContent(ClassFileSourceImpl.java:125) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.DCCommonState.loadClassFileAtPath(DCCommonState.java:117) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.DCCommonState$3.invoke(DCCommonState.java:85) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.DCCommonState$3.invoke(DCCommonState.java:82) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.util.collections.LazyMap.get(LazyMap.java:44) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.util.collections.LazyExceptionRetainingMap.get(LazyExceptionRetainingMap.java:19) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.DCCommonState.getClassFile(DCCommonState.java:183) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.state.DCCommonState.getClassFile(DCCommonState.java:198) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.entities.constantpool.ConstantPoolEntryMethodRef.getMethodPrototype(ConstantPoolEntryMethodRef.java:82) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.analysis.opgraph.Op02WithProcessedDataAndRefs.createStatement(Op02WithProcessedDataAndRefs.java:1197) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.analysis.opgraph.Op02WithProcessedDataAndRefs.access$100(Op02WithProcessedDataAndRefs.java:57) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.analysis.opgraph.Op02WithProcessedDataAndRefs$11.call(Op02WithProcessedDataAndRefs.java:2080) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.analysis.opgraph.Op02WithProcessedDataAndRefs$11.call(Op02WithProcessedDataAndRefs.java:2077) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.util.graph.AbstractGraphVisitorFI.process(AbstractGraphVisitorFI.java:60) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.analysis.opgraph.Op02WithProcessedDataAndRefs.convertToOp03List(Op02WithProcessedDataAndRefs.java:2089) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.CodeAnalyser.getAnalysisInner(CodeAnalyser.java:469) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.CodeAnalyser.getAnalysisOrWrapFail(CodeAnalyser.java:278) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.bytecode.CodeAnalyser.getAnalysis(CodeAnalyser.java:201) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.entities.attributes.AttributeCode.analyse(AttributeCode.java:94) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.entities.Method.analyse(Method.java:531) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.entities.ClassFile.analyseMid(ClassFile.java:1050) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.entities.ClassFile.analyseTop(ClassFile.java:942) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.Driver.doClass(Driver.java:84) ~[cfr-0.152.jar:0.152]
        at org.benf.cfr.reader.CfrDriverImpl.analyse(CfrDriverImpl.java:78) ~[cfr-0.152.jar:0.152]
leibnitz27 commented 2 years ago

That's most odd - I expect a ClassNotFound exception, not a NoClassDefFoundError - for example - here's a java 8 program which uses com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel, which no longer exists in java 11.

TestFoo.txt

C:\code\cfr\target\classes>"c:\Program Files\Java\jdk-11.0.13\bin"\java  org.benf.cfr.reader.Main c:\code\enumexample\out\production\enumexample\org\benf\test\TestFoo.class
/*
 * Decompiled with CFR 0.152-SNAPSHOT (64c9458).
 *
 * Could not load the following classes:
 *  com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel
 */
package org.benf.test;

import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel;

public class TestFoo {
    public void doFoo(int ini) {
        NimbusLookAndFeel test = new NimbusLookAndFeel();
    }
}
leibnitz27 commented 2 years ago

The correct behaviour here would just be to catch Throwable instead of Exception in the outer path, but I'd really like to see this failing - @tzfun can you attach a sample class? (rename to .txt to get github to stop complaining)

leibnitz27 commented 2 years ago

The spec of Class.ForName does indeed say it can throw a linkage error, so seems reasonable to trap this. Would be nice to have the test case though ;)

Marcono1234 commented 2 years ago

Would be nice to have the test case though ;)

Don't have a test case for this, but tzfun mentioned in the description that the decompiled class has imported Hibernate. To me that sounds like they had some Hibernate classes also on their classpath while calling CFR, but somehow their classpath is in a broken state causing loading of ServiceRegistry to fail. And CFR's getContentByFromReflectedClass (which might only have been intended for JDK classes?) loaded this third-party class. I therefore doubt that you can reproduce this merely with the JDK, but this issue can probably happen when third-party classes are on the classpath, which are also referenced in the class to decompile.