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;
|
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.
|
* A table to hold annotations for method parameters.
|
||||||
*/
|
*/
|
||||||
@ -1146,12 +1152,15 @@ public class ClassReader {
|
|||||||
int newbp = bp + attrlen;
|
int newbp = bp + attrlen;
|
||||||
if (saveParameterNames) {
|
if (saveParameterNames) {
|
||||||
int numEntries = nextByte();
|
int numEntries = nextByte();
|
||||||
|
allParameterAccessFlags = new int[numEntries];
|
||||||
parameterNameIndicesMp = new int[numEntries];
|
parameterNameIndicesMp = new int[numEntries];
|
||||||
parameterAccessFlags = new int[numEntries];
|
parameterAccessFlags = new int[numEntries];
|
||||||
|
int allParamIndex = 0;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (int i = 0; i < numEntries; i++) {
|
for (int i = 0; i < numEntries; i++) {
|
||||||
int nameIndex = nextChar();
|
int nameIndex = nextChar();
|
||||||
int flags = nextChar();
|
int flags = nextChar();
|
||||||
|
allParameterAccessFlags[allParamIndex++] = flags;
|
||||||
if ((flags & (Flags.MANDATED | Flags.SYNTHETIC)) != 0) {
|
if ((flags & (Flags.MANDATED | Flags.SYNTHETIC)) != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1593,7 +1602,16 @@ public class ClassReader {
|
|||||||
if (parameterAnnotations == null) {
|
if (parameterAnnotations == null) {
|
||||||
parameterAnnotations = new ParameterAnnotations[numParameters];
|
parameterAnnotations = new ParameterAnnotations[numParameters];
|
||||||
} else if (parameterAnnotations.length != 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++) {
|
for (int pnum = 0; pnum < numParameters; pnum++) {
|
||||||
if (parameterAnnotations[pnum] == null) {
|
if (parameterAnnotations[pnum] == null) {
|
||||||
@ -2623,7 +2641,8 @@ public class ClassReader {
|
|||||||
char rawFlags = nextChar();
|
char rawFlags = nextChar();
|
||||||
long flags = adjustMethodFlags(rawFlags);
|
long flags = adjustMethodFlags(rawFlags);
|
||||||
Name name = poolReader.getName(nextChar());
|
Name name = poolReader.getName(nextChar());
|
||||||
Type type = poolReader.getType(nextChar());
|
Type descriptorType = poolReader.getType(nextChar());
|
||||||
|
Type type = descriptorType;
|
||||||
if (currentOwner.isInterface() &&
|
if (currentOwner.isInterface() &&
|
||||||
(flags & ABSTRACT) == 0 && !name.equals(names.clinit)) {
|
(flags & ABSTRACT) == 0 && !name.equals(names.clinit)) {
|
||||||
if (majorVersion > Version.V52.major ||
|
if (majorVersion > Version.V52.major ||
|
||||||
@ -2640,6 +2659,7 @@ public class ClassReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateMethodType(name, type);
|
validateMethodType(name, type);
|
||||||
|
boolean forceLocal = false;
|
||||||
if (name == names.init && currentOwner.hasOuterInstance()) {
|
if (name == names.init && currentOwner.hasOuterInstance()) {
|
||||||
// Sometimes anonymous classes don't have an outer
|
// Sometimes anonymous classes don't have an outer
|
||||||
// instance, however, there is no reliable way to tell so
|
// 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
|
// 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
|
// won't pass the "hasOuterInstance" check above, but those that don't have an
|
||||||
// enclosing method (i.e. from initializers) will pass that check.
|
// 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)
|
if (!currentOwner.name.isEmpty() && !local)
|
||||||
type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()),
|
type = new MethodType(adjustMethodParams(flags, type.getParameterTypes()),
|
||||||
type.getReturnType(),
|
type.getReturnType(),
|
||||||
@ -2668,6 +2689,7 @@ public class ClassReader {
|
|||||||
currentOwner = prevOwner;
|
currentOwner = prevOwner;
|
||||||
}
|
}
|
||||||
validateMethodType(name, m.type);
|
validateMethodType(name, m.type);
|
||||||
|
adjustParameterAnnotations(m, descriptorType, forceLocal);
|
||||||
setParameters(m, type);
|
setParameters(m, type);
|
||||||
|
|
||||||
if (Integer.bitCount(rawFlags & (PUBLIC | PRIVATE | PROTECTED)) > 1)
|
if (Integer.bitCount(rawFlags & (PUBLIC | PRIVATE | PROTECTED)) > 1)
|
||||||
@ -2795,17 +2817,141 @@ public class ClassReader {
|
|||||||
nameIndexMp++;
|
nameIndexMp++;
|
||||||
annotationIndex++;
|
annotationIndex++;
|
||||||
}
|
}
|
||||||
if (parameterAnnotations != null && parameterAnnotations.length != annotationIndex) {
|
Assert.check(parameterAnnotations == null ||
|
||||||
throw badClassFile("bad.runtime.invisible.param.annotations", sym);
|
parameterAnnotations.length == annotationIndex);
|
||||||
}
|
|
||||||
Assert.checkNull(sym.params);
|
Assert.checkNull(sym.params);
|
||||||
sym.params = params.toList();
|
sym.params = params.toList();
|
||||||
parameterAnnotations = null;
|
parameterAnnotations = null;
|
||||||
parameterNameIndicesLvt = null;
|
parameterNameIndicesLvt = null;
|
||||||
parameterNameIndicesMp = null;
|
parameterNameIndicesMp = null;
|
||||||
|
allParameterAccessFlags = null;
|
||||||
parameterAccessFlags = 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.
|
* Creates the parameter at the position {@code mpIndex} in the parameter list of the owning method.
|
||||||
* Flags are optionally read from the MethodParameters attribute.
|
* Flags are optionally read from the MethodParameters attribute.
|
||||||
|
@ -2547,8 +2547,17 @@ compiler.misc.bad.enclosing.class=\
|
|||||||
compiler.misc.bad.enclosing.method=\
|
compiler.misc.bad.enclosing.method=\
|
||||||
bad enclosing method attribute for class {0}
|
bad enclosing method attribute for class {0}
|
||||||
|
|
||||||
compiler.misc.bad.runtime.invisible.param.annotations=\
|
# 0: file name
|
||||||
bad RuntimeInvisibleParameterAnnotations attribute: {0}
|
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=\
|
compiler.misc.bad.const.pool.tag=\
|
||||||
bad constant pool tag: {0}
|
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.constant.value # bad class file
|
||||||
compiler.misc.bad.enclosing.class # bad class file
|
compiler.misc.bad.enclosing.class # bad class file
|
||||||
compiler.misc.bad.enclosing.method # 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.signature # bad class file
|
||||||
compiler.misc.bad.requires.flag # bad class file
|
compiler.misc.bad.requires.flag # bad class file
|
||||||
compiler.misc.bad.utf8.byte.sequence.at # bad class file
|
compiler.misc.bad.utf8.byte.sequence.at # bad class file
|
||||||
|
Loading…
Reference in New Issue
Block a user