Guardsquare/proguard

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

Code

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