JDK-8024694 : javac always expects signature attribute for enum constructors
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 7u6
  • Priority: P4
  • Status: In Progress
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2013-04-04
  • Updated: 2017-05-19
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_majorUnresolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
1.7.0_04, 1.7.0_06

ADDITIONAL OS VERSION INFORMATION :
RHEL 5.8


EXTRA RELEVANT SYSTEM CONFIGURATION :
  Bug is OS unrelated

A DESCRIPTION OF THE PROBLEM :
Javac denies to compile code containing references to enum types, if the enum type constructor has annotated parameters and the classes were compiled by Eclipse compiler for Java.

Javac complains about  " bad class file "  and  " bad signature " .

It looks like javac expects that enum constructors always have generic signature attribute and tries to use the signature attribute to decode parameter annotations.

Please see the full discussion and code examples here:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=388314


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class MyMain {
public static void main(String[] args){
System.out.println( UseAnnotation.COMPLETE );
}
}

import javax.annotation.Nonnull;
public enum UseAnnotation {
  COMPLETE( " finished " );

  private final String mString;

  UseAnnotation (@Nonnull String aString)  {
    mString = aString;
  }

  public @Nonnull String getString()  {
    return mString;
  }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
There is no workarounds except to remove annotations from the enum constructor parameters.
Comments
ILW=MLM => P4
23-05-2014

Release team: Approved for deferral to 9
21-10-2013

ILV: MMH -> P3
18-10-2013

ok to defer for reasons of compatibility with jdk5+ and time required to fix and sufficiently test the "new" behaviour.
17-10-2013

8-defer-request: javac has emitted class files with the above situation this way since JDK 5. This may be incorrect, but it was what has long been done and tooling may have been built up to expect this behavior. Given where we are in the JDK 8 schedule, I don't think it is prudent to change how code is emitted here since there will be little time to gather feedback and react if there is too much behavioral compatibility impact. However, I think this is a good issue to pursue in JDK 9.
17-10-2013

@Jan: Correct, it is not prohibited, and a Signature attribute is perfectly capable of representing class, method, and field signatures with no type parameters or type arguments.
14-10-2013

@Alex, just to double-check: it is not prohibited to generate the Signature attribute in cases it is not mandatory, correct? I would like to separate concerns here and file a separate bug for the Signature attribute for (enum) constructors, probably for a future release.
14-10-2013

Due to compatibility concerns, we may need to limit generating the proper attributes only for target >= 8.
08-10-2013

In the JVMS, the RuntimeVisibleParameterAnnotations attribute has always stored annotations 1:1 with the parameter types in the method descriptor. The Signature attribute is irrelevant when writing and reading annotations - often it will not even exist. I don't mind if javac accepts class files with RuntimeVisibleParameterAnnotations attributes with fewer entries than the method descriptor, but it should not emit them. The Signature attribute can perfectly well describe fewer parameters than the method descriptor. In general, this can happen for every ctor of a non-private inner member class which has parameterized types among its formal parameter types. The Signature attribute is exposed through Executable#getGenericParameterTypes, so writing more parameters into a Signature attribute will be visible to reflective clients.
30-09-2013

@Alex, may I check if my understanding is correct and have a few questions? I looked at JVMS-4.7.18 (which talks about the RuntimeVisibleParameterAnnotations attribute), in particular at the description of the 'num_parameters' item and my understanding is that the 'parameter_annotations' item should contain an entry for each parameter of the "enclosing" method as defined by the method's method descriptor (i.e. as many entries as there are ParameterDescriptors in the MethodDescriptor). Is that correct? Or is there a possibility to include only entries for TypeSignatures mentioned in the method's Signature attribute (which, as I understand JVMS-4.3.4, is allowed to specify less TypeSignatures than there are ParameterDescriptors in the MethodDescriptor). Would it be a problem if javac accepted RuntimeVisibleParameterAnnotations with less entries than there are ParameterDescriptors in the MethodDescriptor for reasons of compatibility with existing classfiles (javac currently generates the RuntimeVisibleParameterAnnotations based on the Signature, not the method descriptor)? Thanks.
27-09-2013

@Alex, I think that the signature if generated should have the same arguments as the descriptor. In other case the compiler will need to be special cased: method.type = readDescriptor(); if (Enum.init and has signature_attribute) { method.type = readIncompleteSignature(method.type, 2); //2 is the number of parameters that won't be represented in the signature. } else { method.type = readSignature(); } I think this can be done but will be error prone. More when there are other attributes that depend on the parameters, number / order. For example as I mentioned before RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations. The code here will also be signature dependent. if (Enum.init and has signature_attribute) { realParams = params; } else { realParams = extra_params + params } forEachParam(generateParamterAnnotation()); So I think that from the compiler point of view, it's better to have a signature that describes all parameters, no matter if they are added by the compiler or no. So far in the compiler we have this difference between the descriptor and the signature only for enum's <init>, the case of anonymous inner classes <init> could be another strong candidate but here the signature is not generated, special cased, and you can't add Runtime*ParameterAnnotations to them.
20-09-2013

@ Alex, interesting comment about the Signature, thanks. Well, yes the term similar cases is not very well defined, sorry. I was referring to Enum constructors in general. Also related to the Signature, there can be more bugs like this in javac because when it finds a signature in a method, the method type is modified so if in some cases the Signature can contain less information than the descriptor then this can be a source of problems.
18-09-2013

You say "Actually the problem is not the the existence of the Signature in the constructor but the fact that this signature is incorrect." On the one hand, the Signature attribute is intended to support Core Reflection (class file view of the world), so one can argue that all three types from the descriptor should be encoded in Signature. But it would arguably be more useful for compilers (source code view of the world) if Signature encoded only Double, since that's the type of the argument that source code must pass to the enum constructor. So, I would hesitate to say that the Signature attribute is "incorrect". All that said, I think your plan to not write Signature is acceptable. Please be sure to define "similar cases" here.
18-09-2013

Enum types are not generic. An enum declaration does not have a type parameter section so does not declare type variables, which is the defining characteristic of a generic type. The direct superclass of an enum type E is the parameterized type Enum<E> (JLS 8.9), so E's class file must have a Signature attribute storing a class signature - but E is still not generic. There is no need for an enum declaration's constructor to have a Signature attribute storing a method signature if 1) the constructor isn't generic and 2) its formal parameter types are neither parameterized types nor type variables. It's a bug if javac expects a Signature attribute for the constructor written above.
18-09-2013

I think this one will need ccc, because the change will imply that already compiled classes similar to the one in the bug report won't compile anymore. Also the generated code will change. Actually the problem is not the the existence of the Signature in the constructor but the fact that this signature is incorrect. I did a small modification to the UseAnnotation enum: import javax.annotation.Nonnull; public enum UseAnnotation { COMPLETE(3.2); private final Double t; UseAnnotation (@Nonnull Double d) { t = d; } } the javap output for it is: private UseAnnotation(java.lang.Double); descriptor: (Ljava/lang/String;ILjava/lang/Double;)V flags: ACC_PRIVATE Code: stack=3, locals=4, args_size=4 0: aload_0 1: aload_1 2: iload_2 3: invokespecial #6 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 6: aload_0 7: aload_3 8: putfield #7 // Field t:Ljava/lang/Double; 11: return LineNumberTable: line 10: 0 line 11: 6 line 12: 11 Signature: #30 // (Ljava/lang/Double;)V RuntimeVisibleParameterAnnotations: parameter 0: 0: #32() Check the difference between descriptor and signature, they are defining two different method types. As attributes are read in the appearance order: 1. The type is obtained from the descriptor, so we are dealing with a method with 3 arguments: String, int, Double. 2. The type is "refined" from the signature so we are dealing with a method with 1 argument: Double. 3. The RuntimeVisibleParameterAnnotations is applied to Double. I think this problem has been lurking there till today because Enums constructors are private and invoked from the <clinit> method, in other case I guess this problem would have shown up before. See also that the attribute RuntimeVisibleParameterAnnotations says, apply to parameter 0 annotation at #32, parameter 0, I think the bug is actually here. Things are more crazy if you check the output for Eclipse for the same class in this comment: private UseAnnotation(java.lang.String, int, java.lang.Double); descriptor: (Ljava/lang/String;ILjava/lang/Double;)V flags: ACC_PRIVATE RuntimeVisibleParameterAnnotations: parameter 0: 0: #33() Code: stack=3, locals=4, args_size=4 0: aload_0 1: aload_1 2: iload_2 3: invokespecial #34 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 6: aload_0 7: aload_3 8: putfield #37 // Field t:Ljava/lang/Double; 11: return LineNumberTable: line 10: 0 line 11: 6 line 12: 11 Again the same output for attribute RuntimeVisibleParameterAnnotations which in this case as there is no signature attribute and thus the method type can't change should be: parameter 0: parameter 1: parameter 2: 0: #33() // means here no name, value pairs for this annotation. I think the real bug here is in the generation of this attribute by both compilers, in the case of javac could be "fine" because the type is changed by the signature and thus the annotation will be applied to the correct parameter. In the case of Eclipse is totally wrong. So the fix should be: 1. don't write the Signature annotation in this case and similar cases. 2. write the RuntimeVisibleParameterAnnotations correctly So from the specification if you have n parameters and the annotation is applied to the last parameter, you should have n-1 "empty" entries for the n-1 first parameters and at the end the annotation for the last parameter. So for Eclipse users, after fixing this bug, classes created with Eclipse will still produce and error when loaded with javac, because the generation of the RuntimeVisibleParameterAnnotations in Eclipse seems to be broken in this case at least for ecj-3.7.1.jar.
18-09-2013

@Alex, thanks for your comment.
18-09-2013

Note that all enum classes are generic since they extend java.lang.Enum which has an F-bounded type parameter: Enum<E extends Enum<E>>.
13-09-2013