Generics from interfaces are removed
GithubUser8080 opened this issue · 7 comments
Assume interface in a library
interface T Validated<T> {}
and class in injar
class Model implements Validated<List<String>>{}
Proguard removes the generic information, resulting in
class Model implements Validated
This causes errors in jakarta validators. I have tried using --keep (my classes), --keepattribute Signature etc etc, nothing seems to work. I have also tried to disable obfuscation and optimization with no results.
This did not happen with proguard 6.1, it started after upgrade to proguard 7.4.1
Hello,
Can you give a more complete example of the incorrect behavior you observe (Input jar + your full proguard config)? I tried reproducing the issue you describe, but was not able to observe the same behavior.
I compiled a class with class Model implements Validated<List<String>>{}
as source, and using -keepattributes Signature
in my config, the signature attribute was preserved in that class when processing using ProGuard 7.4.2 .
Thank you for the response. I am looking into creating a small project that reproduces it (if it does) as i cannot send the code itself.
At the moment all i can say is that it's a basic JPA entity, the only thing in my config is keep class (the class package**), keepattributes ** and injars/outjars/libraries. Nothing else.
Every attribute is being kept, like annotations, except the one interface it implements where the generic type information is removed. Are there any logs that i can use so that proguard tells me exactly what it does to each class?
I have managed to reproduce this
It is a minimal play framework app with ebean.
One folder contains the project code (basically a new play template). The other folder, has the built project, you could do this yourself with sbt dist.
In the built folder i have included a minimal proguard config (please replace the jmods folder path) that supposedly ignores the Model and keeps the signature attribute. In the result.jar that will be created you can see that in models.Model
implements Constraints.Validatable<List<ValidationError>>
has become
implements EntityBean, Constraints.Validatable
EntityBean is added by the ebean framework during compilation.
Hey, thanks for the reproducing project. I can indeed see what's going on now. It seems like this is actually rather tricky, since it might be caused by the ebean framework not following typical javac
conventions.
What I see in your input is that the Model
class has the following line describing its super/interface classes:
public class models.Model extends java.lang.Object implements play.data.validation.Constraints$Validatable<java.util.List<play.data.validation.ValidationError>>
I think this information comes from the attached Signature attribute, which refers to the following signature: Ljava/lang/Object;Lplay/data/validation/Constraints$Validatable<Ljava/util/List<Lplay/data/validation/ValidationError;>;>;
But, a few lines below we see that Model
actually has 2 interfaces:
interfaces: 2, fields: 4, methods: 28, attributes: 4
Based on the parsed class format I can see this missing interface is io/ebean/bean/EntityBean
. In ProGuard we verify and remove corrupt signatures (in this case it is removed here), to ensure we don't cause any internal errors down the line.
Experimenting a bit with javac
I can see that javac
always puts all interfaces in the Signature
attribute. Possibly the ebean framework only updates the interfaces, but not the Signature
attribute causing this mismatch. From our point of view, we would consider this a bug in the ebean framework.
Thank you for looking into this. As far as i know indeed ebean only changes the interfaces without the signature, but i will investigate further.
Even if that is the case, the affected interface is the one from play which is completely standard. Is it assumed that the incorrect addition of one interface causes proguard to alter the other one too? Furthermore this worked with proguard 6.1 but we had to upgrade due to a JDK upgrade, so perhaps it is a bug in both sides?
There's two relevant parts here. In the java class file format we have an array of interfaces that a class implements. In this case Model
implements play.data.validation.Constraints$Validatable<java.util.List<play.data.validation.ValidationError>>
and io/ebean/bean/EntityBean
. Another relevant part is a Signature
attribute, which will be attached to classes when they contain generic type information. The spec for that is here , it has the following format: FormalTypeParametersopt SuperclassSignature SuperinterfaceSignature*
. In this case, the Signature
attribute is Ljava/lang/Object;Lplay/data/validation/Constraints$Validatable<Ljava/util/List<Lplay/data/validation/ValidationError;>;>;
, where an interface is missing in the attribute.
Based on the example you gave, it looks like the ebean framework is updating the first part, the array of interfaces that a class extends. (Unfortunately, I haven't really found a good way to actually view this, since the displayed signature seems to display the Signature
attribute instead when it is present) But it is not updating the second part, the Signature
attribute, which implicitly also contains the interfaces a class extends.
Based on this, we consider it a bug purely in the ebean framework. It's possible that the behavior with regards to these invalid inputs have changed, but we don't intend to support these.
Thank you, closing this