8334870: javac does not accept classfiles with certain permitted RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations attributes
Reviewed-by: vromero
This commit is contained in:
parent
56387a0981
commit
5e822c24bb
@ -218,6 +218,12 @@ public class ClassReader {
|
||||
*/
|
||||
int[] parameterAccessFlags;
|
||||
|
||||
/**
|
||||
* A table to hold the access flags of the method parameters,
|
||||
* for all parameters including synthetic and mandated ones.
|
||||
*/
|
||||
int[] allParameterAccessFlags;
|
||||
|
||||
/**
|
||||
* A table to hold annotations for method parameters.
|
||||
*/
|
||||
@ -1146,12 +1152,15 @@ public class ClassReader {
|
||||
int newbp = bp + attrlen;
|
||||
if (saveParameterNames) {
|
||||
int numEntries = nextByte();
|
||||
allParameterAccessFlags = new int[numEntries];
|
||||
parameterNameIndicesMp = new int[numEntries];
|
||||
parameterAccessFlags = new int[numEntries];
|
||||
int allParamIndex = 0;
|
||||
int index = 0;
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
int nameIndex = nextChar();
|
||||
int flags = nextChar();
|
||||
allParameterAccessFlags[allParamIndex++] = flags;
|
||||
if ((flags & (Flags.MANDATED | Flags.SYNTHETIC)) != 0) {
|
||||
continue;
|
||||
}
|
||||
@ -1593,7 +1602,16 @@ public class ClassReader {
|
||||
if (parameterAnnotations == null) {
|
||||
parameterAnnotations = new ParameterAnnotations[numParameters];
|
||||
} else if (parameterAnnotations.length != numParameters) {
|
||||
throw badClassFile("bad.runtime.invisible.param.annotations", meth);
|
||||
//the RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations
|
||||
//provide annotations for a different number of parameters, ignore:
|
||||
if (lintClassfile) {
|
||||
log.warning(LintCategory.CLASSFILE, Warnings.RuntimeVisibleInvisibleParamAnnotationsMismatch(currentClassFile));
|
||||
}
|
||||
for (int pnum = 0; pnum < numParameters; pnum++) {
|
||||
readAnnotations();
|
||||
}
|
||||
parameterAnnotations = null;
|
||||
return ;
|
||||
}
|
||||
for (int pnum = 0; pnum < numParameters; pnum++) {
|
||||
if (parameterAnnotations[pnum] == null) {
|
||||
@ -2623,7 +2641,8 @@ public class ClassReader {
|
||||
char rawFlags = nextChar();
|
||||
long flags = adjustMethodFlags(rawFlags);
|
||||
Name name = poolReader.getName(nextChar());
|
||||
Type type = poolReader.getType(nextChar());
|
||||
Type descriptorType = poolReader.getType(nextChar());
|
||||
Type type = descriptorType;
|
||||
if (currentOwner.isInterface() &&
|
||||
(flags & ABSTRACT) == 0 && !name.equals(names.clinit)) {
|
||||
if (majorVersion > Version.V52.major ||
|
||||
@ -2640,6 +2659,7 @@ public class ClassReader {
|
||||
}
|
||||
}
|
||||
validateMethodType(name, type);
|
||||
boolean forceLocal = false;
|
||||
if (name == names.init && currentOwner.hasOuterInstance()) {
|
||||
// Sometimes anonymous classes don't have an outer
|
||||
// instance, however, there is no reliable way to tell so
|
||||
@ -2647,7 +2667,8 @@ public class ClassReader {
|
||||
// ditto for local classes. Local classes that have an enclosing method set
|
||||
// won't pass the "hasOuterInstance" check above, but those that don't have an
|
||||
// enclosing method (i.e. from initializers) will pass that check.
|
||||
boolean local = !currentOwner.owner.members().includes(currentOwner, LookupKind.NON_RECURSIVE);
|
||||
boolean local = forceLocal =
|
||||
!currentOwner.owner.members().includes(currentOwner, LookupKind.NON_RECURSIVE);
|
||||
if (!currentOwner.name.isEmpty() && !local)
|
||||
type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()),
|
||||
type.getReturnType(),
|
||||
@ -2668,6 +2689,7 @@ public class ClassReader {
|
||||
currentOwner = prevOwner;
|
||||
}
|
||||
validateMethodType(name, m.type);
|
||||
adjustParameterAnnotations(m, descriptorType, forceLocal);
|
||||
setParameters(m, type);
|
||||
|
||||
if (Integer.bitCount(rawFlags & (PUBLIC | PRIVATE | PROTECTED)) > 1)
|
||||
@ -2795,17 +2817,141 @@ public class ClassReader {
|
||||
nameIndexMp++;
|
||||
annotationIndex++;
|
||||
}
|
||||
if (parameterAnnotations != null && parameterAnnotations.length != annotationIndex) {
|
||||
throw badClassFile("bad.runtime.invisible.param.annotations", sym);
|
||||
}
|
||||
Assert.check(parameterAnnotations == null ||
|
||||
parameterAnnotations.length == annotationIndex);
|
||||
Assert.checkNull(sym.params);
|
||||
sym.params = params.toList();
|
||||
parameterAnnotations = null;
|
||||
parameterNameIndicesLvt = null;
|
||||
parameterNameIndicesMp = null;
|
||||
allParameterAccessFlags = null;
|
||||
parameterAccessFlags = null;
|
||||
}
|
||||
|
||||
void adjustParameterAnnotations(MethodSymbol sym, Type methodDescriptor,
|
||||
boolean forceLocal) {
|
||||
if (parameterAnnotations == null) {
|
||||
return ;
|
||||
}
|
||||
|
||||
//the specification for Runtime(In)VisibleParameterAnnotations does not
|
||||
//enforce any mapping between the method parameters and the recorded
|
||||
//parameter annotation. Attempt a number of heuristics to adjust the
|
||||
//adjust parameterAnnotations to the percieved number of parameters:
|
||||
|
||||
int methodParameterCount = sym.type.getParameterTypes().size();
|
||||
|
||||
if (methodParameterCount == parameterAnnotations.length) {
|
||||
//we've got exactly as many parameter annotations as are parameters
|
||||
//of the method (after considering a possible Signature attribute),
|
||||
//no need to do anything. the parameter creation code will use
|
||||
//the 1-1 mapping to restore the annotations:
|
||||
return ;
|
||||
}
|
||||
|
||||
if (allParameterAccessFlags != null) {
|
||||
//MethodParameters attribute present, use it:
|
||||
|
||||
//count the number of non-synthetic and non-mandatory parameters:
|
||||
int realParameters = 0;
|
||||
|
||||
for (int i = 0; i < allParameterAccessFlags.length; i++) {
|
||||
if ((allParameterAccessFlags[i] & (SYNTHETIC | MANDATED)) == 0) {
|
||||
realParameters++;
|
||||
}
|
||||
}
|
||||
|
||||
int methodDescriptorParameterCount = methodDescriptor.getParameterTypes().size();
|
||||
|
||||
if (realParameters == parameterAnnotations.length &&
|
||||
allParameterAccessFlags.length == methodDescriptorParameterCount) {
|
||||
//if we have parameter annotations for each non-synthetic/mandatory parameter,
|
||||
//and if Signature was not present, expand the parameterAnnotations to cover
|
||||
//all the method descriptor's parameters:
|
||||
if (sym.type == methodDescriptor) {
|
||||
ParameterAnnotations[] newParameterAnnotations =
|
||||
new ParameterAnnotations[methodParameterCount];
|
||||
int srcIndex = 0;
|
||||
|
||||
for (int i = 0; i < methodParameterCount; i++) {
|
||||
if ((allParameterAccessFlags[i] & (SYNTHETIC | MANDATED)) == 0) {
|
||||
newParameterAnnotations[i] = parameterAnnotations[srcIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
parameterAnnotations = newParameterAnnotations;
|
||||
} else {
|
||||
dropParameterAnnotations();
|
||||
}
|
||||
} else if (realParameters == methodParameterCount &&
|
||||
methodDescriptorParameterCount == parameterAnnotations.length &&
|
||||
allParameterAccessFlags.length == methodDescriptorParameterCount) {
|
||||
//if there are as many parameter annotations as parameters in
|
||||
//the method descriptor, and as many real parameters as parameters
|
||||
//in the method's type (after accounting for Signature), shrink
|
||||
//the parameterAnnotations to only cover the parameters from
|
||||
//the method's type:
|
||||
ParameterAnnotations[] newParameterAnnotations =
|
||||
new ParameterAnnotations[methodParameterCount];
|
||||
int targetIndex = 0;
|
||||
|
||||
for (int i = 0; i < parameterAnnotations.length; i++) {
|
||||
if ((allParameterAccessFlags[i] & (SYNTHETIC | MANDATED)) == 0) {
|
||||
newParameterAnnotations[targetIndex++] = parameterAnnotations[i];
|
||||
}
|
||||
}
|
||||
|
||||
parameterAnnotations = newParameterAnnotations;
|
||||
} else {
|
||||
dropParameterAnnotations();
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
if (!sym.isConstructor()) {
|
||||
//if the number of parameter annotations and the number of parameters
|
||||
//don't match, we don't have any heuristics to map one to the other
|
||||
//unless the method is a constructor:
|
||||
dropParameterAnnotations();
|
||||
return ;
|
||||
}
|
||||
|
||||
if (sym.owner.isEnum()) {
|
||||
if (methodParameterCount == parameterAnnotations.length + 2 &&
|
||||
sym.type == methodDescriptor) {
|
||||
//handle constructors of enum types without the Signature attribute -
|
||||
//there are the two synthetic parameters (name and ordinal) in the
|
||||
//constructor, but there may be only parameter annotations for the
|
||||
//real non-synthetic parameters:
|
||||
ParameterAnnotations[] newParameterAnnotations = new ParameterAnnotations[parameterAnnotations.length + 2];
|
||||
System.arraycopy(parameterAnnotations, 0, newParameterAnnotations, 2, parameterAnnotations.length);
|
||||
parameterAnnotations = newParameterAnnotations;
|
||||
return ;
|
||||
}
|
||||
} else if (sym.owner.isDirectlyOrIndirectlyLocal() || forceLocal) {
|
||||
//local class may capture the enclosing instance (as the first parameter),
|
||||
//and local variables (as trailing parameters)
|
||||
//if there are less parameter annotations than parameters, put the existing
|
||||
//ones starting with offset:
|
||||
if (methodParameterCount > parameterAnnotations.length &&
|
||||
sym.type == methodDescriptor) {
|
||||
ParameterAnnotations[] newParameterAnnotations = new ParameterAnnotations[methodParameterCount];
|
||||
System.arraycopy(parameterAnnotations, 0, newParameterAnnotations, 1, parameterAnnotations.length);
|
||||
parameterAnnotations = newParameterAnnotations;
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
//no heuristics worked, drop the annotations:
|
||||
dropParameterAnnotations();
|
||||
}
|
||||
|
||||
private void dropParameterAnnotations() {
|
||||
parameterAnnotations = null;
|
||||
if (lintClassfile) {
|
||||
log.warning(LintCategory.CLASSFILE, Warnings.RuntimeInvisibleParameterAnnotations(currentClassFile));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates the parameter at the position {@code mpIndex} in the parameter list of the owning method.
|
||||
* Flags are optionally read from the MethodParameters attribute.
|
||||
|
@ -2547,8 +2547,17 @@ compiler.misc.bad.enclosing.class=\
|
||||
compiler.misc.bad.enclosing.method=\
|
||||
bad enclosing method attribute for class {0}
|
||||
|
||||
compiler.misc.bad.runtime.invisible.param.annotations=\
|
||||
bad RuntimeInvisibleParameterAnnotations attribute: {0}
|
||||
# 0: file name
|
||||
compiler.warn.runtime.visible.invisible.param.annotations.mismatch=\
|
||||
the length of parameters in RuntimeVisibleParameterAnnotations attribute and \
|
||||
RuntimeInvisibleParameterAnnotations attribute in: {0} \
|
||||
do not match, ignoring both attributes
|
||||
|
||||
# 0: file name
|
||||
compiler.warn.runtime.invisible.parameter.annotations=\
|
||||
the RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations attributes \
|
||||
in: {0} \
|
||||
cannot be mapped to the method''s parameters
|
||||
|
||||
compiler.misc.bad.const.pool.tag=\
|
||||
bad constant pool tag: {0}
|
||||
|
@ -1,320 +0,0 @@
|
||||
class T {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
49; // version
|
||||
[73] { // Constant Pool
|
||||
; // first element is empty
|
||||
Utf8 "T"; // #1 at 0x0A
|
||||
class #1; // #2 at 0x1A
|
||||
Utf8 "Ljava/lang/Enum<LT;>;"; // #3 at 0x1D
|
||||
Utf8 "java/lang/Enum"; // #4 at 0x41
|
||||
class #4; // #5 at 0x52
|
||||
Utf8 "T.java"; // #6 at 0x55
|
||||
Utf8 "T1"; // #7 at 0x61
|
||||
Utf8 "LT;"; // #8 at 0x66
|
||||
Utf8 "T2"; // #9 at 0x78
|
||||
Utf8 "T3"; // #10 at 0x7D
|
||||
Utf8 "myName"; // #11 at 0x82
|
||||
Utf8 "Ljava/lang/String;"; // #12 at 0x8B
|
||||
Utf8 "$VALUES"; // #13 at 0xA0
|
||||
Utf8 "[LT;"; // #14 at 0xAA
|
||||
Utf8 "values"; // #15 at 0xBD
|
||||
Utf8 "()[LT;"; // #16 at 0xC6
|
||||
NameAndType #13 #14; // #17 at 0xDB
|
||||
Field #2 #17; // #18 at 0xE0
|
||||
class #14; // #19 at 0xE5
|
||||
Utf8 "clone"; // #20 at 0xE8
|
||||
Utf8 "()Ljava/lang/Object;"; // #21 at 0xF0
|
||||
NameAndType #20 #21; // #22 at 0x0107
|
||||
Method #19 #22; // #23 at 0x010C
|
||||
Utf8 "valueOf"; // #24 at 0x0111
|
||||
Utf8 "(Ljava/lang/String;)LT;"; // #25 at 0x011B
|
||||
Utf8 "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;"; // #26 at 0x0141
|
||||
NameAndType #24 #26; // #27 at 0x0179
|
||||
Method #5 #27; // #28 at 0x017E
|
||||
Utf8 "name"; // #29 at 0x0183
|
||||
Utf8 "getName"; // #30 at 0x018A
|
||||
Utf8 "()Ljava/lang/String;"; // #31 at 0x0194
|
||||
NameAndType #11 #12; // #32 at 0x01AB
|
||||
Field #2 #32; // #33 at 0x01B0
|
||||
Utf8 "this"; // #34 at 0x01B5
|
||||
Utf8 "<init>"; // #35 at 0x01BC
|
||||
Utf8 "(Ljava/lang/String;ILjava/lang/String;)V"; // #36 at 0x01C5
|
||||
Utf8 "LNotNull;"; // #37 at 0x01F0
|
||||
Utf8 "java/lang/IllegalArgumentException"; // #38 at 0x0216
|
||||
class #38; // #39 at 0x023B
|
||||
Utf8 "Argument 0 for @NotNull parameter of T.<init> must not be null"; // #40 at 0x023E
|
||||
String #40; // #41 at 0x028B
|
||||
Utf8 "(Ljava/lang/String;)V"; // #42 at 0x028E
|
||||
NameAndType #35 #42; // #43 at 0x02A6
|
||||
Method #39 #43; // #44 at 0x02AB
|
||||
Utf8 "(Ljava/lang/String;I)V"; // #45 at 0x02B0
|
||||
NameAndType #35 #45; // #46 at 0x02C9
|
||||
Method #5 #46; // #47 at 0x02CE
|
||||
Utf8 "<clinit>"; // #48 at 0x02D3
|
||||
Utf8 "()V"; // #49 at 0x02DE
|
||||
String #7; // #50 at 0x02E4
|
||||
Utf8 "type1"; // #51 at 0x02E7
|
||||
String #51; // #52 at 0x02EF
|
||||
NameAndType #35 #36; // #53 at 0x02F2
|
||||
Method #2 #53; // #54 at 0x02F7
|
||||
NameAndType #7 #8; // #55 at 0x02FC
|
||||
Field #2 #55; // #56 at 0x0301
|
||||
String #9; // #57 at 0x0306
|
||||
Utf8 "type2"; // #58 at 0x0309
|
||||
String #58; // #59 at 0x0311
|
||||
NameAndType #9 #8; // #60 at 0x0314
|
||||
Field #2 #60; // #61 at 0x0319
|
||||
String #10; // #62 at 0x031E
|
||||
Utf8 "type3"; // #63 at 0x0321
|
||||
String #63; // #64 at 0x0329
|
||||
NameAndType #10 #8; // #65 at 0x032C
|
||||
Field #2 #65; // #66 at 0x0331
|
||||
Utf8 "Code"; // #67 at 0x0336
|
||||
Utf8 "LineNumberTable"; // #68 at 0x033D
|
||||
Utf8 "LocalVariableTable"; // #69 at 0x034F
|
||||
Utf8 "Signature"; // #70 at 0x0364
|
||||
Utf8 "RuntimeInvisibleParameterAnnotations"; // #71 at 0x0370
|
||||
Utf8 "SourceFile"; // #72 at 0x0397
|
||||
} // Constant Pool
|
||||
|
||||
0x4031; // access
|
||||
#2;// this_cpx
|
||||
#5;// super_cpx
|
||||
|
||||
[0] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[5] { // fields
|
||||
{ // Member at 0x03AE
|
||||
0x4019; // access
|
||||
#7; // name_cpx
|
||||
#8; // sig_cpx
|
||||
[0] { // Attributes
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x03B6
|
||||
0x4019; // access
|
||||
#9; // name_cpx
|
||||
#8; // sig_cpx
|
||||
[0] { // Attributes
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x03BE
|
||||
0x4019; // access
|
||||
#10; // name_cpx
|
||||
#8; // sig_cpx
|
||||
[0] { // Attributes
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x03C6
|
||||
0x0012; // access
|
||||
#11; // name_cpx
|
||||
#12; // sig_cpx
|
||||
[0] { // Attributes
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x03CE
|
||||
0x101A; // access
|
||||
#13; // name_cpx
|
||||
#14; // sig_cpx
|
||||
[0] { // Attributes
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // fields
|
||||
|
||||
[5] { // methods
|
||||
{ // Member at 0x03D8
|
||||
0x0019; // access
|
||||
#15; // name_cpx
|
||||
#16; // sig_cpx
|
||||
[1] { // Attributes
|
||||
Attr(#67, 34) { // Code at 0x03E0
|
||||
1; // max_stack
|
||||
0; // max_locals
|
||||
Bytes[10]{
|
||||
0xB20012B60017C000;
|
||||
0x13B0;
|
||||
};
|
||||
[0] { // Traps
|
||||
} // end Traps
|
||||
[1] { // Attributes
|
||||
Attr(#68, 6) { // LineNumberTable at 0x03FC
|
||||
[1] { // LineNumberTable
|
||||
0 9; // at 0x0408
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x0408
|
||||
0x0009; // access
|
||||
#24; // name_cpx
|
||||
#25; // sig_cpx
|
||||
[1] { // Attributes
|
||||
Attr(#67, 52) { // Code at 0x0410
|
||||
2; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[10]{
|
||||
0x12022AB8001CC000;
|
||||
0x02B0;
|
||||
};
|
||||
[0] { // Traps
|
||||
} // end Traps
|
||||
[2] { // Attributes
|
||||
Attr(#69, 12) { // LocalVariableTable at 0x042C
|
||||
[1] { // LocalVariableTable
|
||||
0 10 29 12 0; // at 0x043E
|
||||
}
|
||||
} // end LocalVariableTable
|
||||
;
|
||||
Attr(#68, 6) { // LineNumberTable at 0x043E
|
||||
[1] { // LineNumberTable
|
||||
0 9; // at 0x044A
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x044A
|
||||
0x0001; // access
|
||||
#30; // name_cpx
|
||||
#31; // sig_cpx
|
||||
[1] { // Attributes
|
||||
Attr(#67, 47) { // Code at 0x0452
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[5]{
|
||||
0x2AB40021B0;
|
||||
};
|
||||
[0] { // Traps
|
||||
} // end Traps
|
||||
[2] { // Attributes
|
||||
Attr(#69, 12) { // LocalVariableTable at 0x0469
|
||||
[1] { // LocalVariableTable
|
||||
0 5 34 8 0; // at 0x047B
|
||||
}
|
||||
} // end LocalVariableTable
|
||||
;
|
||||
Attr(#68, 6) { // LineNumberTable at 0x047B
|
||||
[1] { // LineNumberTable
|
||||
0 17; // at 0x0487
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x0487
|
||||
0x0002; // access
|
||||
#35; // name_cpx
|
||||
#36; // sig_cpx
|
||||
[3] { // Attributes
|
||||
Attr(#67, 86) { // Code at 0x048F
|
||||
3; // max_stack
|
||||
4; // max_locals
|
||||
Bytes[26]{
|
||||
0x2BC7000DBB002759;
|
||||
0x1229B7002CBF2A2B;
|
||||
0x1CB7002F2A2DB500;
|
||||
0x21B1;
|
||||
};
|
||||
[0] { // Traps
|
||||
} // end Traps
|
||||
[2] { // Attributes
|
||||
Attr(#69, 22) { // LocalVariableTable at 0x04BB
|
||||
[2] { // LocalVariableTable
|
||||
14 12 34 8 0; // at 0x04CD
|
||||
14 12 29 12 3; // at 0x04D7
|
||||
}
|
||||
} // end LocalVariableTable
|
||||
;
|
||||
Attr(#68, 14) { // LineNumberTable at 0x04D7
|
||||
[3] { // LineNumberTable
|
||||
14 20; // at 0x04E3
|
||||
20 21; // at 0x04E7
|
||||
25 22; // at 0x04EB
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
;
|
||||
Attr(#70, 2) { // Signature at 0x04EB
|
||||
#42;
|
||||
} // end Signature
|
||||
;
|
||||
Attr(#71, 11) { // RuntimeInvisibleParameterAnnotations at 0x04F3
|
||||
[3]b { // parameters
|
||||
[1] { // annotations
|
||||
{ // annotation
|
||||
#37;
|
||||
[0] { // element_value_pairs
|
||||
} // element_value_pairs
|
||||
} // annotation
|
||||
}
|
||||
;
|
||||
[0] { // annotations
|
||||
}
|
||||
;
|
||||
[0] { // annotations
|
||||
}
|
||||
}
|
||||
} // end RuntimeInvisibleParameterAnnotations
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member at 0x0504
|
||||
0x0008; // access
|
||||
#48; // name_cpx
|
||||
#49; // sig_cpx
|
||||
[1] { // Attributes
|
||||
Attr(#67, 107) { // Code at 0x050C
|
||||
5; // max_stack
|
||||
0; // max_locals
|
||||
Bytes[71]{
|
||||
0xBB00025912320312;
|
||||
0x34B70036B30038BB;
|
||||
0x000259123904123B;
|
||||
0xB70036B3003DBB00;
|
||||
0x0259123E051240B7;
|
||||
0x0036B3004206BD00;
|
||||
0x025903B200385359;
|
||||
0x04B2003D535905B2;
|
||||
0x004253B30012B1;
|
||||
};
|
||||
[0] { // Traps
|
||||
} // end Traps
|
||||
[1] { // Attributes
|
||||
Attr(#68, 18) { // LineNumberTable at 0x0565
|
||||
[4] { // LineNumberTable
|
||||
0 10; // at 0x0571
|
||||
15 11; // at 0x0575
|
||||
30 12; // at 0x0579
|
||||
45 9; // at 0x057D
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[2] { // Attributes
|
||||
Attr(#70, 2) { // Signature at 0x057F
|
||||
#3;
|
||||
} // end Signature
|
||||
;
|
||||
Attr(#72, 2) { // SourceFile at 0x0587
|
||||
#6;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class T
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 6435291
|
||||
* @summary javac shouldn't throw NPE while compiling invalid RuntimeInvisibleParameterAnnotations
|
||||
* @author Wei Tao
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.code
|
||||
* jdk.compiler/com.sun.tools.javac.comp
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build T
|
||||
* @run main/othervm T6435291
|
||||
*/
|
||||
|
||||
import com.sun.tools.javac.api.JavacTaskImpl;
|
||||
import com.sun.tools.javac.code.ClassFinder.BadClassFile;
|
||||
import com.sun.tools.javac.code.Symtab;
|
||||
import com.sun.tools.javac.util.Names;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
public class T6435291 {
|
||||
public static void main(String... args) {
|
||||
javax.tools.JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
|
||||
JavacTaskImpl task = (JavacTaskImpl)tool.getTask(null, null, null, null, null, null);
|
||||
Symtab syms = Symtab.instance(task.getContext());
|
||||
Names names = Names.instance(task.getContext());
|
||||
task.ensureEntered();
|
||||
try {
|
||||
syms.enterClass(syms.unnamedModule, names.fromString("T")).complete();
|
||||
} catch (BadClassFile e) {
|
||||
System.err.println("Passed: expected completion failure " + e.getClass().getName());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed: unexpected exception");
|
||||
}
|
||||
throw new RuntimeException("Failed: no error reported");
|
||||
}
|
||||
}
|
@ -0,0 +1,699 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8024694 8334870
|
||||
* @summary Check javac can handle various Runtime(In)VisibleParameterAnnotations attribute combinations
|
||||
* @enablePreview
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
* jdk.compiler/com.sun.tools.javac.main
|
||||
* jdk.compiler/com.sun.tools.javac.util
|
||||
* @build toolbox.ToolBox toolbox.JavacTask
|
||||
* @run main ParameterAnnotations
|
||||
*/
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.ClassModel;
|
||||
import java.lang.classfile.ClassTransform;
|
||||
import java.lang.classfile.MethodBuilder;
|
||||
import java.lang.classfile.MethodElement;
|
||||
import java.lang.classfile.MethodTransform;
|
||||
import java.lang.classfile.attribute.MethodParametersAttribute;
|
||||
import java.lang.classfile.attribute.RuntimeInvisibleParameterAnnotationsAttribute;
|
||||
import java.lang.classfile.attribute.RuntimeVisibleParameterAnnotationsAttribute;
|
||||
import java.lang.classfile.attribute.SignatureAttribute;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
|
||||
import toolbox.TestRunner;
|
||||
import toolbox.JavacTask;
|
||||
import toolbox.Task;
|
||||
import toolbox.ToolBox;
|
||||
|
||||
public class ParameterAnnotations extends TestRunner {
|
||||
|
||||
ToolBox tb;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
new ParameterAnnotations().runTests();
|
||||
}
|
||||
|
||||
ParameterAnnotations() {
|
||||
super(System.err);
|
||||
tb = new ToolBox();
|
||||
}
|
||||
|
||||
public void runTests() throws Exception {
|
||||
runTests(m -> new Object[] { Paths.get(m.getName()) });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnum(Path base) throws Exception {
|
||||
//not parameterized:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public enum E {
|
||||
A(0);
|
||||
E(@Visible @Invisible long i) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"E",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
//parameterized:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public enum E {
|
||||
A(0);
|
||||
<T> E(@Visible @Invisible long i) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"E",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
//not parameterized, and no Signature attribute:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public enum E {
|
||||
A(0);
|
||||
E(@Visible @Invisible long i) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"E",
|
||||
NO_SIGNATURE,
|
||||
"java.lang.String, int, @Invisible @Visible long");
|
||||
//not parameterized, and no Signature and MethodParameters attribute:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public enum E {
|
||||
A(0);
|
||||
E(@Visible @Invisible long i) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"E",
|
||||
NO_SIGNATURE_NO_METHOD_PARAMETERS,
|
||||
"java.lang.String, int, @Invisible @Visible long");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInnerClass(Path base) throws Exception {
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString(); //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$I",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public class I {
|
||||
public <T> I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString(); //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$I",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString(); //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$I",
|
||||
NO_SIGNATURE,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString(); //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$I",
|
||||
NO_SIGNATURE_NO_METHOD_PARAMETERS,
|
||||
"@Invisible @Visible long");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCapturingLocal(Path base) throws Exception {
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public void test(int i) {
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public void test(int i) {
|
||||
class I {
|
||||
public <T> I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public void test(int i) {
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
NO_SIGNATURE,
|
||||
"T, @Invisible @Visible long, int");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public void test(int i) {
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
NO_SIGNATURE_NO_METHOD_PARAMETERS,
|
||||
"T, @Invisible @Visible long, int");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
{
|
||||
int i = 0;
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
{
|
||||
int i = 0;
|
||||
class I {
|
||||
public <T> I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"@Invisible @Visible long");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
{
|
||||
int i = 0;
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
NO_SIGNATURE,
|
||||
"T, @Invisible @Visible long, int");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
{
|
||||
int i = 0;
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
NO_SIGNATURE_NO_METHOD_PARAMETERS,
|
||||
"T, @Invisible @Visible long, int");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyntheticTests(Path base) throws Exception {
|
||||
//Signature attribute will defined one parameter, but the
|
||||
//Runtime(In)VisibleParameterAnnotations will define 3 parameters:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public void test(int i) {
|
||||
class I {
|
||||
public I(@Visible @Invisible long l) {}
|
||||
public String toString() {
|
||||
return T.this.toString() + i; //force outer this capture
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T$1I",
|
||||
new MethodTransform() {
|
||||
@Override
|
||||
public void accept(MethodBuilder builder, MethodElement element) {
|
||||
if (element instanceof RuntimeInvisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 1;
|
||||
builder.accept(RuntimeInvisibleParameterAnnotationsAttribute.of(List.of(List.of(), annos.parameterAnnotations().get(0), List.of())));
|
||||
} else if (element instanceof RuntimeVisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 1;
|
||||
builder.accept(RuntimeVisibleParameterAnnotationsAttribute.of(List.of(List.of(), annos.parameterAnnotations().get(0), List.of())));
|
||||
} else {
|
||||
builder.accept(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
"@Invisible @Visible long");
|
||||
//no Signature attribute, no synthetic parameters,
|
||||
//but less entries in Runtime(In)VisibleParameterAnnotations than parameters
|
||||
//no way to map anything:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public T(int i, @Visible @Invisible long l, String s) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T",
|
||||
new MethodTransform() {
|
||||
@Override
|
||||
public void accept(MethodBuilder builder, MethodElement element) {
|
||||
if (element instanceof RuntimeInvisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 3;
|
||||
builder.accept(RuntimeInvisibleParameterAnnotationsAttribute.of(List.of(annos.parameterAnnotations().get(1))));
|
||||
} else if (element instanceof RuntimeVisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 3;
|
||||
builder.accept(RuntimeVisibleParameterAnnotationsAttribute.of(List.of(annos.parameterAnnotations().get(1))));
|
||||
} else {
|
||||
builder.accept(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
"int, long, java.lang.String",
|
||||
"- compiler.warn.runtime.invisible.parameter.annotations: T.class",
|
||||
"1 warning");
|
||||
//no Signature attribute, no synthetic parameters,
|
||||
//but more entries in Runtime(In)VisibleParameterAnnotations than parameters
|
||||
//no way to map anything:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public T(@Visible @Invisible long l) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T",
|
||||
new MethodTransform() {
|
||||
@Override
|
||||
public void accept(MethodBuilder builder, MethodElement element) {
|
||||
if (element instanceof RuntimeInvisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 1;
|
||||
builder.accept(RuntimeInvisibleParameterAnnotationsAttribute.of(List.of(List.of(), annos.parameterAnnotations().get(0), List.of())));
|
||||
} else if (element instanceof RuntimeVisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 1;
|
||||
builder.accept(RuntimeVisibleParameterAnnotationsAttribute.of(List.of(List.of(), annos.parameterAnnotations().get(0), List.of())));
|
||||
} else {
|
||||
builder.accept(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
"long",
|
||||
"- compiler.warn.runtime.invisible.parameter.annotations: T.class",
|
||||
"1 warning");
|
||||
//mismatched lengths on RuntimeVisibleParameterAnnotations and
|
||||
//RuntimeInvisibleParameterAnnotations:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public class T {
|
||||
public T(@Visible @Invisible long l) {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"T",
|
||||
new MethodTransform() {
|
||||
@Override
|
||||
public void accept(MethodBuilder builder, MethodElement element) {
|
||||
if (element instanceof RuntimeInvisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 1;
|
||||
builder.accept(annos); //keep intact
|
||||
} else if (element instanceof RuntimeVisibleParameterAnnotationsAttribute annos) {
|
||||
assert annos.parameterAnnotations().size() == 1;
|
||||
builder.accept(RuntimeVisibleParameterAnnotationsAttribute.of(List.of(List.of(), annos.parameterAnnotations().get(0), List.of())));
|
||||
} else {
|
||||
builder.accept(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
"long",
|
||||
"- compiler.warn.runtime.visible.invisible.param.annotations.mismatch: T.class",
|
||||
"1 warning");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecord(Path base) throws Exception {
|
||||
//implicit constructor:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public record R(int i, @Visible @Invisible long l, String s) {
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"R",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"int, @Invisible @Visible long, java.lang.String");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public record R(int i, @Visible @Invisible long l, String s) {
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"R",
|
||||
NO_SIGNATURE,
|
||||
"int, @Invisible @Visible long, java.lang.String");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public record R(int i, @Visible @Invisible long l, String s) {
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"R",
|
||||
NO_SIGNATURE_NO_METHOD_PARAMETERS,
|
||||
"int, @Invisible @Visible long, java.lang.String");
|
||||
//compact constructor:
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public record R(int i, @Visible @Invisible long l, String s) {
|
||||
public R {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"R",
|
||||
MethodTransform.ACCEPT_ALL,
|
||||
"int, @Invisible @Visible long, java.lang.String");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public record R(int i, @Visible @Invisible long l, String s) {
|
||||
public R {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"R",
|
||||
NO_SIGNATURE,
|
||||
"int, @Invisible @Visible long, java.lang.String");
|
||||
doTest(base,
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
public record R(int i, @Visible @Invisible long l, String s) {
|
||||
public R {}
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Visible {}
|
||||
@interface Invisible {}
|
||||
""",
|
||||
"R",
|
||||
NO_SIGNATURE_NO_METHOD_PARAMETERS,
|
||||
"int, @Invisible @Visible long, java.lang.String");
|
||||
}
|
||||
|
||||
private MethodTransform NO_SIGNATURE =
|
||||
MethodTransform.dropping(element -> element instanceof SignatureAttribute);
|
||||
|
||||
private MethodTransform NO_SIGNATURE_NO_METHOD_PARAMETERS =
|
||||
MethodTransform.dropping(element -> element instanceof SignatureAttribute ||
|
||||
element instanceof MethodParametersAttribute);
|
||||
|
||||
private void doTest(Path base, String code, String binaryNameToCheck,
|
||||
MethodTransform changeConstructor, String expectedOutput,
|
||||
String... expectedDiagnostics) throws Exception {
|
||||
Path current = base.resolve(".");
|
||||
Path src = current.resolve("src");
|
||||
Path classes = current.resolve("classes");
|
||||
tb.writeJavaFiles(src, code);
|
||||
|
||||
Files.createDirectories(classes);
|
||||
|
||||
new JavacTask(tb)
|
||||
.outdir(classes)
|
||||
.files(tb.findJavaFiles(src))
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
|
||||
Path classfile = classes.resolve(binaryNameToCheck + ".class");
|
||||
ClassFile cf = ClassFile.of();
|
||||
|
||||
ClassModel model = cf.parse(classfile);
|
||||
|
||||
byte[] newClassFile = cf.transformClass(model,
|
||||
ClassTransform.transformingMethods(m -> m.methodName()
|
||||
.equalsString("<init>"),
|
||||
changeConstructor));
|
||||
|
||||
try (OutputStream out = Files.newOutputStream(classfile)) {
|
||||
out.write(newClassFile);
|
||||
}
|
||||
|
||||
Task.Result result = new JavacTask(tb)
|
||||
.options("-classpath", classes.toString(),
|
||||
"-processor", TestAP.class.getName(),
|
||||
"-XDrawDiagnostics",
|
||||
"-Xlint:classfile")
|
||||
.outdir(classes)
|
||||
.classes(binaryNameToCheck)
|
||||
.run(Task.Expect.SUCCESS)
|
||||
.writeAll();
|
||||
List<String> out = result.getOutputLines(Task.OutputKind.STDOUT);
|
||||
if (!out.equals(List.of(expectedOutput))) {
|
||||
throw new AssertionError("Expected: " + List.of(expectedOutput) + ", but got: " + out);
|
||||
}
|
||||
List<String> diagnostics =
|
||||
new ArrayList<>(result.getOutputLines(Task.OutputKind.DIRECT));
|
||||
diagnostics.remove("");
|
||||
if (!diagnostics.equals(List.of(expectedDiagnostics))) {
|
||||
throw new AssertionError("Expected: " + List.of(expectedDiagnostics) + ", but got: " + diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
@SupportedAnnotationTypes("*")
|
||||
public static final class TestAP extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
for (TypeElement clazz : ElementFilter.typesIn(roundEnv.getRootElements())) {
|
||||
for (ExecutableElement el : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
|
||||
String sep = "";
|
||||
|
||||
for (VariableElement p : el.getParameters()) {
|
||||
System.out.print(sep);
|
||||
if (!p.getAnnotationMirrors().isEmpty()) {
|
||||
System.out.print(p.getAnnotationMirrors()
|
||||
.stream()
|
||||
.map(m -> m.toString())
|
||||
.collect(Collectors.joining(" ")));
|
||||
System.out.print(" ");
|
||||
}
|
||||
System.out.print(p.asType());
|
||||
sep = ", ";
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latest();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -55,7 +55,8 @@ compiler.misc.bad.constant.range # bad class file
|
||||
compiler.misc.bad.constant.value # bad class file
|
||||
compiler.misc.bad.enclosing.class # bad class file
|
||||
compiler.misc.bad.enclosing.method # bad class file
|
||||
compiler.misc.bad.runtime.invisible.param.annotations # bad class file
|
||||
compiler.warn.runtime.invisible.parameter.annotations # bad class file
|
||||
compiler.warn.runtime.visible.invisible.param.annotations.mismatch # bad class file
|
||||
compiler.misc.bad.signature # bad class file
|
||||
compiler.misc.bad.requires.flag # bad class file
|
||||
compiler.misc.bad.utf8.byte.sequence.at # bad class file
|
||||
|
Loading…
Reference in New Issue
Block a user