proguard
proguard copied to clipboard
Proguard 7.0.1 inappropriately keeps classes that override default interface methods
A demonstration source code is attached.
To execute the demo:
- Unzip the ZIP file, which contains two .java source files and a config.pro.
- Compile the two .java source files with
java
. - Zip the two resulting .class files into
Plain.jar
:zip Plain.jar *.class
- Run Proguard:
bin/proguard.sh @config.pro
Expected behavior:
The class ConsumerWithError
is not used by any "keep" entrypoints and should be removed during shrinking.
Actual behavior:
ConsumerWithError
is invoked by ConsumerWithError.accept (12:19)
implements java.util.function.Consumer.accept
is a library method.
Hi @drosenbauer ! Thanks for the clear report. I successfully executed your demo, and I agree that I would expect this interface to be removed during shrinking. When I modify your ConsumerWithError
interface as follows:
- remove all method invocations of methods that are in
ConsumerWithError
- modify the return type of methods with return type
ConsumerWithError
(namelyandThen
) - AND remove the anonymous function
return (a) -> { ... }
, then the interface is removed. My hypothesis of what is happening here is the following: - ProGuard marks the
Consumer
interface (including its methods) as 'used, as it is extended by the
ConsumerWithError` interface/class. - Then ProGuard finds out that the
ConsumerWithError
class overrides certain methods (with a default implementation) of theConsumer
class. - ProGuard notices that the
ConsumerWithError
class is used in (at least one of) these methods. It thus marks all classes that are referenced from the interface methods ... even the interface itself, without checking whether there even exists a concrete class implementing the interface. It is, however, clear that theConsumerWithError
interface is never implemented by a concrete class, but ProGuard doesn't seem to check this, therefore keeping the interface without actual usage.
I hope this clears things up a bit! I will make a ticket for this, so it can hopefully be fixed in a future release. Have a nice day!
a very simple test case from https://stackoverflow.com/questions/11584159/is-there-a-way-to-make-runnables-run-throw-an-exception/53398040#53398040
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {
@Override
default void run() throws RuntimeException {
try {
runThrows();
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
void runThrows() throws E;
}
Proguard incorrectly keep this class
[proguard] Shrinking...
[proguard] Explaining why classes and class members are being kept...
[proguard] packageCheckedRunnable
[proguard] is invoked by package.CheckedRunnable: void run() (8:23)
[proguard] implements java.lang.Runnable: void run()
[proguard] is a library method.