Skip to content

Commit ed3272c

Browse files
jddarcycushon
andcommittedJan 26, 2024
8042981: Strip type annotations in Types' utility methods
Co-authored-by: Liam Miller-Cushon <cushon@openjdk.org> Reviewed-by: cushon, jjg, jlahoda
1 parent 6d18562 commit ed3272c

File tree

5 files changed

+449
-11
lines changed

5 files changed

+449
-11
lines changed
 

‎src/java.compiler/share/classes/javax/lang/model/util/Types.java

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,10 @@
3535
/**
3636
* Utility methods for operating on types.
3737
*
38+
* Where a method returns a type mirror or a collection of type
39+
* mirrors, any type mirrors represent types with no type annotations,
40+
* unless otherwise indicated.
41+
*
3842
* <p><b>Compatibility Note:</b> Methods may be added to this interface
3943
* in future releases of the platform.
4044
*
@@ -153,6 +157,8 @@ public interface Types {
153157
* the direct supertypes of a type mirror representing {@code
154158
* java.lang.Object}.
155159
*
160+
* Annotations on the direct super types are preserved.
161+
*
156162
* @param t the type being examined
157163
* @return the direct supertypes, or an empty list if none
158164
* @throws IllegalArgumentException if given a type for an executable, package, or module
@@ -235,6 +241,8 @@ public interface Types {
235241
/**
236242
* {@return an array type with the specified component type}
237243
*
244+
* Annotations on the component type are preserved.
245+
*
238246
* @param componentType the component type
239247
* @throws IllegalArgumentException if the component type is not valid for
240248
* an array
@@ -245,6 +253,8 @@ public interface Types {
245253
* {@return a new wildcard type} Either of the wildcard's
246254
* bounds may be specified, or neither, but not both.
247255
*
256+
* Annotations on the bounds are preserved.
257+
*
248258
* @param extendsBound the extends (upper) bound, or {@code null} if none
249259
* @param superBound the super (lower) bound, or {@code null} if none
250260
* @throws IllegalArgumentException if bounds are not valid
@@ -260,6 +270,8 @@ WildcardType getWildcardType(TypeMirror extendsBound,
260270
* for example, this method may be used to get the
261271
* parameterized type {@code Set<String>}.
262272
*
273+
* Annotations on the type arguments are preserved.
274+
*
263275
* <p> The number of type arguments must either equal the
264276
* number of the type element's formal type parameters, or must be
265277
* zero. If zero, and if the type element is generic,
@@ -291,6 +303,8 @@ WildcardType getWildcardType(TypeMirror extendsBound,
291303
* to get the type {@code Outer<String>}, and then invoking
292304
* this method.
293305
*
306+
* Annotations on the type arguments are preserved.
307+
*
294308
* <p> If the containing type is a parameterized type,
295309
* the number of type arguments must equal the
296310
* number of {@code typeElem}'s formal type parameters.
@@ -324,4 +338,29 @@ DeclaredType getDeclaredType(DeclaredType containing,
324338
* for the given type
325339
*/
326340
TypeMirror asMemberOf(DeclaredType containing, Element element);
341+
342+
/**
343+
* {@return a type mirror equivalent to the argument, but with no annotations}
344+
* If the type mirror is a composite type, such as an array type
345+
* or a wildcard type, any constitute types, such as the
346+
* component type of an array and the type of the bounds of a
347+
* wildcard type, also have no annotations, recursively.
348+
*
349+
* <p>For most kinds of type mirrors, the result of
350+
* {@snippet lang="java" :
351+
* types.isSameType(typeMirror, types.stripAnnotations(typeMirror))
352+
* }
353+
* is {@code true}. The predicate is {@code false} on wildcard
354+
* types for {@linkplain #isSameType(TypeMirror, TypeMirror)
355+
* reasons discussed elsewhere}.
356+
*
357+
* @param t the type mirror
358+
* @param <T> the specific type of type mirror
359+
* @implSpec
360+
* The default implementation throws {@code UnsupportedOperationException}.
361+
* @since 23
362+
*/
363+
default <T extends TypeMirror> T stripAnnotations(T t) {
364+
throw new UnsupportedOperationException();
365+
}
327366
}

‎src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java

+24-7
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public Type baseType() {
340340
* it should not be used outside this class.
341341
*/
342342
protected Type typeNoMetadata() {
343-
return metadata.isEmpty() ? this : baseType();
343+
return metadata.isEmpty() ? this : stripMetadata();
344344
}
345345

346346
/**
@@ -426,25 +426,42 @@ public Type stripMetadata() {
426426
return accept(stripMetadata, null);
427427
}
428428
//where
429+
/**
430+
* Note: this visitor only needs to handle cases where
431+
* 'contained' types can be annotated. These cases are
432+
* described in JVMS 4.7.20.2 and are : classes (for type
433+
* parameters and enclosing types), wildcards, and arrays.
434+
*/
429435
private static final TypeMapping<Void> stripMetadata = new StructuralTypeMapping<Void>() {
430436
@Override
431437
public Type visitClassType(ClassType t, Void aVoid) {
432-
return super.visitClassType((ClassType)t.typeNoMetadata(), aVoid);
438+
return super.visitClassType((ClassType) dropMetadata(t), aVoid);
433439
}
434440

435441
@Override
436442
public Type visitArrayType(ArrayType t, Void aVoid) {
437-
return super.visitArrayType((ArrayType)t.typeNoMetadata(), aVoid);
443+
return super.visitArrayType((ArrayType) dropMetadata(t), aVoid);
438444
}
439445

440446
@Override
441-
public Type visitTypeVar(TypeVar t, Void aVoid) {
442-
return super.visitTypeVar((TypeVar)t.typeNoMetadata(), aVoid);
447+
public Type visitWildcardType(WildcardType wt, Void aVoid) {
448+
return super.visitWildcardType((WildcardType) dropMetadata(wt), aVoid);
443449
}
444450

445451
@Override
446-
public Type visitWildcardType(WildcardType wt, Void aVoid) {
447-
return super.visitWildcardType((WildcardType)wt.typeNoMetadata(), aVoid);
452+
public Type visitType(Type t, Void aVoid) {
453+
return dropMetadata(t);
454+
}
455+
456+
private static Type dropMetadata(Type t) {
457+
if (t.getMetadata().isEmpty()) {
458+
return t;
459+
}
460+
Type baseType = t.baseType();
461+
if (baseType.getMetadata().isEmpty()) {
462+
return baseType;
463+
}
464+
return baseType.cloneWithMetadata(List.nil());
448465
}
449466
};
450467

‎src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacTypes.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public TypeMirror erasure(TypeMirror t) {
134134
TypeKind kind = t.getKind();
135135
if (kind == TypeKind.PACKAGE || kind == TypeKind.MODULE)
136136
throw new IllegalArgumentException(t.toString());
137-
return types.erasure((Type)t).stripMetadataIfNeeded();
137+
return types.erasure((Type)t).stripMetadata();
138138
}
139139

140140
@DefinedBy(Api.LANGUAGE_MODEL)
@@ -155,7 +155,7 @@ public PrimitiveType unboxedType(TypeMirror t) {
155155
@DefinedBy(Api.LANGUAGE_MODEL)
156156
public TypeMirror capture(TypeMirror t) {
157157
validateTypeNotIn(t, EXEC_OR_PKG_OR_MOD);
158-
return types.capture((Type)t).stripMetadataIfNeeded();
158+
return types.capture((Type)t).stripMetadata();
159159
}
160160

161161
@DefinedBy(Api.LANGUAGE_MODEL)
@@ -304,6 +304,13 @@ public TypeMirror asMemberOf(DeclaredType containing, Element element) {
304304
}
305305

306306

307+
@DefinedBy(Api.LANGUAGE_MODEL)
308+
@SuppressWarnings("unchecked")
309+
public <T extends TypeMirror> T stripAnnotations(T t) {
310+
return (T)((Type) t).stripMetadata();
311+
}
312+
313+
307314
private static final Set<TypeKind> EXEC_OR_PKG_OR_MOD =
308315
EnumSet.of(TypeKind.EXECUTABLE, TypeKind.PACKAGE, TypeKind.MODULE);
309316

‎test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java

+71-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
2626
import javax.annotation.processing.*;
2727
import javax.lang.model.SourceVersion;
2828
import javax.lang.model.element.*;
29+
import javax.lang.model.type.*;
2930
import javax.lang.model.util.*;
3031
import static javax.lang.model.SourceVersion.*;
3132

@@ -322,4 +323,73 @@ public void printElements(Writer w, Element... elements) {}
322323
@Override
323324
public boolean isFunctionalInterface(TypeElement type) {return false;}
324325
}
326+
327+
/**
328+
* Vacuous implementation of javax.lang.model.util.Types to aid
329+
* in test development. Methods with defaults in the interface are
330+
* *not* overridden to allow them to be tested.
331+
*/
332+
public static class VacuousTypes implements Types {
333+
public VacuousTypes() {}
334+
335+
@Override
336+
public Element asElement(TypeMirror t) {return null;}
337+
338+
@Override
339+
public boolean isSameType(TypeMirror t1, TypeMirror t2) {return false;}
340+
341+
@Override
342+
public boolean isSubtype(TypeMirror t1, TypeMirror t2) {return false;};
343+
344+
@Override
345+
public boolean isAssignable(TypeMirror t1, TypeMirror t2) {return false;};
346+
347+
@Override
348+
public boolean contains(TypeMirror t1, TypeMirror t2) {return false;};
349+
350+
@Override
351+
public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {return false;}
352+
353+
@Override
354+
public List<? extends TypeMirror> directSupertypes(TypeMirror t) {return null;}
355+
356+
@Override
357+
public TypeMirror erasure(TypeMirror t) {return null;}
358+
359+
@Override
360+
public TypeElement boxedClass(PrimitiveType p) {return null;}
361+
362+
@Override
363+
public PrimitiveType unboxedType(TypeMirror t) {return null;}
364+
365+
@Override
366+
public TypeMirror capture(TypeMirror t) {return null;}
367+
368+
@Override
369+
public PrimitiveType getPrimitiveType(TypeKind kind) {return null;}
370+
371+
@Override
372+
public NullType getNullType() {return null;}
373+
374+
@Override
375+
public NoType getNoType(TypeKind kind) {return null;}
376+
377+
@Override
378+
public ArrayType getArrayType(TypeMirror componentType) {return null;}
379+
380+
@Override
381+
public WildcardType getWildcardType(TypeMirror extendsBound,
382+
TypeMirror superBound) {return null;}
383+
384+
@Override
385+
public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {return null;}
386+
387+
388+
@Override
389+
public DeclaredType getDeclaredType(DeclaredType containing,
390+
TypeElement typeElem, TypeMirror... typeArgs) {return null;}
391+
392+
@Override
393+
public TypeMirror asMemberOf(DeclaredType containing, Element element) {return null;}
394+
}
325395
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
/*
2+
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8042981
27+
* @summary Test if annotations are stripped from the results of Types' methods
28+
* @library /tools/javac/lib
29+
* @modules java.compiler
30+
* jdk.compiler
31+
* @build JavacTestingAbstractProcessor TestAnnotationStripping
32+
* @compile -processor TestAnnotationStripping -proc:only TestAnnotationStripping.java
33+
*/
34+
35+
import java.lang.annotation.*;
36+
import java.util.*;
37+
import static java.util.Objects.*;
38+
import javax.annotation.processing.*;
39+
import javax.lang.model.SourceVersion;
40+
import static javax.lang.model.SourceVersion.*;
41+
import javax.lang.model.*;
42+
import javax.lang.model.element.*;
43+
import javax.lang.model.type.*;
44+
import javax.lang.model.util.*;
45+
import static javax.lang.model.util.ElementFilter.*;
46+
import static javax.tools.Diagnostic.Kind.*;
47+
import static javax.tools.StandardLocation.*;
48+
49+
/**
50+
* Test if annotations are stripped from the results of Types' methods
51+
*/
52+
public class TestAnnotationStripping extends JavacTestingAbstractProcessor {
53+
private Types vacuousTypes = new VacuousTypes();
54+
55+
/**
56+
* Check expected behavior on classes and packages.
57+
*/
58+
public boolean process(Set<? extends TypeElement> annotations,
59+
RoundEnvironment roundEnv) {
60+
if (!roundEnv.processingOver()) {
61+
TypeElement juSetElt = eltUtils.getTypeElement("java.util.Set");
62+
TypeElement testElt = elements.getTypeElement("TestAnnotationStripping");
63+
TypeElement boxElt = elements.getTypeElement("TestAnnotationStripping.Box");
64+
65+
TypeMirror expectedAnnotation = eltUtils.getTypeElement("TestTypeAnnotation").asType();
66+
67+
for (ExecutableElement m :
68+
methodsIn(eltUtils.getTypeElement("HostClass").getEnclosedElements())) {
69+
/*
70+
* The kinds of types include:
71+
*
72+
* arrays
73+
* declared types (classes, interfaces, etc.)
74+
* error types
75+
* executable types
76+
* intersection types
77+
* no-type
78+
* null type
79+
* primitive types
80+
* type variable
81+
* union type
82+
* wildcards
83+
*
84+
* A subset of these can appear at the return type of
85+
* a method. The general methodology is to verify that
86+
* types that can appear as return types when
87+
* annotated with type annotations appear as specified
88+
* as the result of type operations or when new types
89+
* are constructed.
90+
*/
91+
92+
TypeMirror returnType = m.getReturnType();
93+
94+
System.err.println("Checking " + returnType);
95+
96+
testVacuous(returnType);
97+
checkDeepEmptyAnnotations(typeUtils.stripAnnotations(returnType));
98+
99+
checkExpectedTypeAnnotations(returnType, expectedAnnotation);
100+
101+
// Note: the result of Types.asElement is *not*
102+
// checked for its annotations since the return value
103+
// is an Element and not a TypeMirror.
104+
105+
System.err.print("\tcapture()");
106+
checkDeepEmptyAnnotations(typeUtils.capture(returnType));
107+
108+
System.err.print("\terasure()");
109+
checkDeepEmptyAnnotations(typeUtils.erasure(returnType));
110+
111+
System.err.print("\tgetArrayType()");
112+
ArrayType arrayType = typeUtils.getArrayType(returnType);
113+
checkEmptyAnnotations(arrayType);
114+
/*
115+
* "Annotations on the component type are preserved."
116+
*/
117+
checkEqualTypeAndAnnotations(returnType, arrayType.getComponentType());
118+
119+
if (!returnType.getKind().isPrimitive()) {
120+
/*
121+
* For getWildcardType()
122+
* "Annotations on the bounds are preserved."
123+
*/
124+
WildcardType wcType;
125+
checkEmptyAnnotations(wcType = typeUtils.getWildcardType(returnType, null));
126+
checkEqualTypeAndAnnotations(returnType, wcType.getExtendsBound());
127+
128+
checkEmptyAnnotations(wcType = typeUtils.getWildcardType(null, returnType));
129+
checkEqualTypeAndAnnotations(returnType, wcType.getSuperBound());
130+
131+
/*
132+
* For getDeclaredType()
133+
* "Annotations on the type arguments are preserved."
134+
*/
135+
DeclaredType declaredType = typeUtils.getDeclaredType(juSetElt, returnType);
136+
checkEqualTypeAndAnnotations(returnType, declaredType.getTypeArguments().get(0));
137+
138+
// Check both overloads
139+
declaredType = typeUtils.getDeclaredType(typeUtils.getDeclaredType(testElt), // outer type
140+
boxElt,
141+
returnType);
142+
checkEqualTypeAndAnnotations(returnType, declaredType.getTypeArguments().get(0));
143+
}
144+
145+
System.out.println(returnType.getAnnotation(TestTypeAnnotation.class));
146+
System.out.println(returnType.getAnnotationsByType(TestTypeAnnotation.class).length);
147+
TestTypeAnnotation ta = requireNonNull(returnType.getAnnotation(TestTypeAnnotation.class),
148+
returnType.toString());
149+
150+
System.err.println();
151+
System.err.println();
152+
}
153+
154+
if (failures > 0)
155+
throw new RuntimeException(failures + " failures occured.");
156+
}
157+
return true;
158+
}
159+
160+
void testVacuous(TypeMirror tm ) {
161+
try {
162+
var result = vacuousTypes.stripAnnotations(tm);
163+
messager.printError("Unexpected non-exceptional result returned " + result);
164+
} catch(UnsupportedOperationException uoe) {
165+
; // Expected
166+
}
167+
}
168+
169+
private int failures = 0;
170+
171+
void checkExpectedTypeAnnotations(AnnotatedConstruct ac, TypeMirror expectedAnnotation) {
172+
List<? extends AnnotationMirror> annotations = ac.getAnnotationMirrors();
173+
if (annotations.size() != 1) {
174+
failures++;
175+
System.err.println("\t\t\tUnexpected annotations size: " + annotations.size());
176+
} else if (!typeUtils.isSameType(annotations.get(0).getAnnotationType(), expectedAnnotation)) {
177+
failures++;
178+
System.err.println("\t\t\tUnexpected annotations type: " + annotations);
179+
}
180+
}
181+
182+
void checkEmptyAnnotations(AnnotatedConstruct ac) {
183+
System.err.println("\t" + ac);
184+
if (ac == null)
185+
return;
186+
else {
187+
List<? extends AnnotationMirror> annotations = ac.getAnnotationMirrors();
188+
int count = annotations.size();
189+
if (count != 0) {
190+
failures++;
191+
System.err.println(ac.getClass());
192+
System.err.println("\t\t\tUnexpected nonzero annotations size: " + annotations);
193+
}
194+
}
195+
}
196+
197+
void checkDeepEmptyAnnotations(TypeMirror ac) {
198+
System.err.println("\t" + ac);
199+
if (ac == null) {
200+
return;
201+
}
202+
new SimpleTypeVisitor14<Void, Void>() {
203+
@Override
204+
protected Void defaultAction(TypeMirror t, Void o) {
205+
checkEmptyAnnotations(t);
206+
return null;
207+
}
208+
209+
@Override
210+
public Void visitArray(ArrayType t, Void o) {
211+
scan(t.getComponentType());
212+
return super.visitArray(t, o);
213+
}
214+
215+
@Override
216+
public Void visitDeclared(DeclaredType t, Void o) {
217+
scan(t.getEnclosingType());
218+
t.getTypeArguments().stream().forEach(this::scan);
219+
return super.visitDeclared(t, o);
220+
}
221+
222+
@Override
223+
public Void visitTypeVariable(TypeVariable t, Void o) {
224+
// the bounds correspond to the type variable declaration, not its use
225+
// scan(t.getUpperBound());
226+
// scan(t.getLowerBound());
227+
return super.visitTypeVariable(t, o);
228+
}
229+
230+
@Override
231+
public Void visitWildcard(WildcardType t, Void o) {
232+
scan(t.getExtendsBound());
233+
scan(t.getSuperBound());
234+
return super.visitWildcard(t, o);
235+
}
236+
237+
private void scan(TypeMirror t) {
238+
if (t != null) {
239+
visit(t);
240+
}
241+
}
242+
}.visit(ac);
243+
}
244+
245+
void checkEqualTypeAndAnnotations(TypeMirror tm1, TypeMirror tm2) {
246+
if (!typeUtils.isSameType(tm1, tm2)) {
247+
failures++;
248+
System.err.printf("Unequal types %s and %s.%n", tm1, tm2);
249+
}
250+
251+
if (!Objects.equals(tm1.getAnnotationMirrors(), tm1.getAnnotationMirrors())) {
252+
failures++;
253+
System.err.printf("Unequal annotations on and %s.%n", tm1, tm2);
254+
}
255+
}
256+
257+
// Nested class to test getDeclaredType overload.
258+
class Box<T> {
259+
private T contents;
260+
261+
public Box(T t){
262+
contents = t;
263+
}
264+
265+
T value() { return contents;};
266+
}
267+
}
268+
269+
/*
270+
* Class to host annotations for testing
271+
*/
272+
class HostClass {
273+
// Declared type Integer
274+
public static @TestTypeAnnotation("foo") Integer foo() {return null;}
275+
276+
// Primitive type int
277+
public static @TestTypeAnnotation("foo2") int foo2() {return 0;}
278+
279+
public static @TestTypeAnnotation("foo3") String foo3() {return null;}
280+
281+
// Declared raw type Set
282+
public static java.util.@TestTypeAnnotation("foo4")Set foo4() {return null;}
283+
284+
// Array type
285+
public static String @TestTypeAnnotation("foo5")[] foo5() {return null;}
286+
287+
// Declared type Set with instantiated type parameter
288+
public static java.util. @TestTypeAnnotation("foo6") Set < @TestTypeAnnotation("foo7") String> foo6() {return null;}
289+
290+
// Type variable
291+
public static <@TestTypeAnnotation("foo8") T extends @TestTypeAnnotation("foo9") String> @TestTypeAnnotation("foo10") T foo7() {return null;}
292+
293+
// Declared type including wildcard
294+
public static java.util. @TestTypeAnnotation("foo11") Set < @TestTypeAnnotation("foo12") ? extends @TestTypeAnnotation("foo13") Number> foo8() {return null;}
295+
296+
// Type variable with intersection type
297+
public static <@TestTypeAnnotation("foo14") S extends Number & Runnable> @TestTypeAnnotation("foo15") S foo9() {return null;}
298+
299+
}
300+
301+
@Retention(RetentionPolicy.RUNTIME)
302+
@Target(ElementType.TYPE_USE)
303+
@interface TestTypeAnnotation {
304+
String value() default "";
305+
}

0 commit comments

Comments
 (0)
Please sign in to comment.