8356894: Adjust CreateSymbols to properly handle the newly added @jdk.internal.RequiresIdentity

Reviewed-by: vromero, liach
This commit is contained in:
Jan Lahoda 2025-05-23 04:35:29 +00:00
parent 99e01301cd
commit 9d9e41f5b4
12 changed files with 529 additions and 30 deletions

View File

@ -46,6 +46,8 @@ import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.lang.classfile.TypeAnnotation.TargetInfo;
import java.lang.classfile.TypeAnnotation.TypePathComponent;
import java.lang.classfile.attribute.*;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
@ -991,6 +993,12 @@ public class CreateSymbols {
if (desc.runtimeAnnotations != null && !desc.runtimeAnnotations.isEmpty()) {
builder.accept(RuntimeVisibleAnnotationsAttribute.of(createAnnotations(desc.runtimeAnnotations)));
}
if (desc.classTypeAnnotations != null && !desc.classTypeAnnotations.isEmpty()) {
builder.accept(RuntimeInvisibleTypeAnnotationsAttribute.of(createTypeAnnotations(desc.classTypeAnnotations)));
}
if (desc.runtimeTypeAnnotations != null && !desc.runtimeTypeAnnotations.isEmpty()) {
builder.accept(RuntimeVisibleTypeAnnotationsAttribute.of(createTypeAnnotations(desc.runtimeTypeAnnotations)));
}
}
private List<Annotation> createAnnotations(List<AnnotationDescription> desc) {
@ -1066,6 +1074,44 @@ public class CreateSymbols {
default -> throw new IllegalArgumentException(value.getClass().getName());
};
}
private List<TypeAnnotation> createTypeAnnotations(List<TypeAnnotationDescription> desc) {
return desc.stream().map(this::createTypeAnnotation).collect(Collectors.toList());
}
private TypeAnnotation createTypeAnnotation(TypeAnnotationDescription desc) {
Annotation baseAnn = createAnnotation(desc.annotation);
TargetInfo targetInfo = switch ((String) desc.targetInfo.get("targetType")) {
case "CLASS_TYPE_PARAMETER" -> //TODO: test!
TargetInfo.ofClassTypeParameter((int) desc.targetInfo.get("typeParameterIndex"));
case "METHOD_TYPE_PARAMETER" ->
TargetInfo.ofMethodTypeParameter((int) desc.targetInfo.get("typeParameterIndex"));
case "CLASS_EXTENDS" ->
TargetInfo.ofClassExtends((int) desc.targetInfo.get("supertypeIndex"));
case "CLASS_TYPE_PARAMETER_BOUND" ->
TargetInfo.ofClassTypeParameterBound((int) desc.targetInfo.get("typeParameterIndex"),
(int) desc.targetInfo.get("boundIndex"));
case "METHOD_TYPE_PARAMETER_BOUND" ->
TargetInfo.ofMethodTypeParameterBound((int) desc.targetInfo.get("typeParameterIndex"),
(int) desc.targetInfo.get("boundIndex"));
case "METHOD_RETURN" ->
TargetInfo.ofMethodReturn();
case "METHOD_RECEIVER" ->
TargetInfo.ofMethodReceiver();
case "METHOD_FORMAL_PARAMETER" ->
TargetInfo.ofMethodFormalParameter((int) desc.targetInfo.get("formalParameterIndex"));
case "THROWS" ->
TargetInfo.ofThrows((int) desc.targetInfo.get("throwsTargetIndex"));
case "FIELD" ->
TargetInfo.ofField();
case String targetType ->
throw new IllegalStateException("Unsupported targetType: " + targetType);
};
List<TypePathComponent> typePath = desc.typePath.stream().map(d -> TypePathComponent.of(TypePathComponent.Kind.valueOf(d.tag()), d.index())).toList();
return TypeAnnotation.of(targetInfo, typePath, baseAnn);
}
//</editor-fold>
//</editor-fold>
@ -2213,7 +2259,10 @@ public class CreateSymbols {
chd.permittedSubclasses = a.permittedSubclasses().stream().map(ClassEntry::asInternalName).collect(Collectors.toList());
}
case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName();
case RuntimeVisibleTypeAnnotationsAttribute a -> {/* do nothing for now */}
case RuntimeInvisibleTypeAnnotationsAttribute a ->
feature.classTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
case RuntimeVisibleTypeAnnotationsAttribute a ->
feature.runtimeTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
default -> throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName()); // Do nothing
}
@ -2270,6 +2319,31 @@ public class CreateSymbols {
return new AnnotationDescription(annotationType, values);
}
private List<TypeAnnotationDescription> typeAnnotations2Descriptions(List<TypeAnnotation> annos) {
return annos.stream().map(ta -> {
TypeAnnotationDescription desc = new TypeAnnotationDescription();
desc.annotation = annotation2Description(ta.annotation());
desc.targetInfo = new HashMap<>();
desc.targetInfo.put("targetType", ta.targetInfo().targetType().name());
switch (ta.targetInfo()) {
case TypeAnnotation.TypeParameterTarget tpt -> desc.targetInfo.put("typeParameterIndex", tpt.typeParameterIndex());
case TypeAnnotation.SupertypeTarget st -> desc.targetInfo.put("supertypeIndex", st.supertypeIndex());
case TypeAnnotation.TypeParameterBoundTarget tpbt -> {
desc.targetInfo.put("typeParameterIndex", tpbt.typeParameterIndex());
desc.targetInfo.put("boundIndex", tpbt.boundIndex());
}
case TypeAnnotation.EmptyTarget _ -> {
// nothing to write
}
case TypeAnnotation.FormalParameterTarget fpt -> desc.targetInfo.put("formalParameterIndex", fpt.formalParameterIndex());
case TypeAnnotation.ThrowsTarget tt -> desc.targetInfo.put("throwsTargetIndex", tt.throwsTargetIndex());
default -> throw new IllegalStateException(ta.targetInfo().targetType().name());
}
desc.typePath = ta.targetPath().stream().map(tpc -> new TypeAnnotationDescription.TypePathComponentDesc(tpc.typePathKind().name(), tpc.typeArgumentIndex())).toList();
return desc;
}).toList();
}
//</editor-fold>
protected boolean includeEffectiveAccess(ClassList classes, ClassDescription clazz) {
@ -2391,6 +2465,8 @@ public class CreateSymbols {
String versions = "";
List<AnnotationDescription> classAnnotations;
List<AnnotationDescription> runtimeAnnotations;
List<TypeAnnotationDescription> classTypeAnnotations;
List<TypeAnnotationDescription> runtimeTypeAnnotations;
protected void writeAttributes(Appendable output) throws IOException {
if (flags != 0)
@ -2413,6 +2489,18 @@ public class CreateSymbols {
output.append(quote(a.toString(), false));
}
}
if (classTypeAnnotations != null && !classTypeAnnotations.isEmpty()) {
output.append(" classTypeAnnotations ");
for (TypeAnnotationDescription a : classTypeAnnotations) {
output.append(quote(a.toString(), false));
}
}
if (runtimeTypeAnnotations != null && !runtimeTypeAnnotations.isEmpty()) {
output.append(" runtimeTypeAnnotations ");
for (TypeAnnotationDescription a : runtimeTypeAnnotations) {
output.append(quote(a.toString(), false));
}
}
}
protected boolean shouldIgnore(String baselineVersion, String version) {
@ -2442,6 +2530,14 @@ public class CreateSymbols {
if (inRuntimeAnnotations != null) {
runtimeAnnotations = parseAnnotations(inRuntimeAnnotations, new int[1]);
}
String inClassTypeAnnotations = reader.attributes.get("classTypeAnnotations");
if (inClassTypeAnnotations != null) {
classTypeAnnotations = parseTypeAnnotations(inClassTypeAnnotations, new int[1]);
}
String inRuntimeTypeAnnotations = reader.attributes.get("runtimeTypeAnnotations");
if (inRuntimeTypeAnnotations != null) {
runtimeTypeAnnotations = parseTypeAnnotations(inRuntimeTypeAnnotations, new int[1]);
}
}
public abstract boolean read(LineBasedReader reader) throws IOException;
@ -2454,6 +2550,8 @@ public class CreateSymbols {
hash = 89 * hash + Objects.hashCode(this.signature);
hash = 89 * hash + listHashCode(this.classAnnotations);
hash = 89 * hash + listHashCode(this.runtimeAnnotations);
hash = 89 * hash + listHashCode(this.classTypeAnnotations);
hash = 89 * hash + listHashCode(this.runtimeTypeAnnotations);
return hash;
}
@ -2481,6 +2579,12 @@ public class CreateSymbols {
if (!listEquals(this.runtimeAnnotations, other.runtimeAnnotations)) {
return false;
}
if (!listEquals(this.classTypeAnnotations, other.classTypeAnnotations)) {
return false;
}
if (!listEquals(this.runtimeTypeAnnotations, other.runtimeTypeAnnotations)) {
return false;
}
return true;
}
@ -3285,6 +3389,8 @@ public class CreateSymbols {
hash = 59 * hash + Objects.hashCode(this.descriptor);
hash = 59 * hash + Objects.hashCode(this.thrownTypes);
hash = 59 * hash + Objects.hashCode(this.annotationDefaultValue);
hash = 59 * hash + Objects.hashCode(this.classParameterAnnotations);
hash = 59 * hash + Objects.hashCode(this.runtimeParameterAnnotations);
return hash;
}
@ -3309,6 +3415,12 @@ public class CreateSymbols {
if (!Objects.equals(this.annotationDefaultValue, other.annotationDefaultValue)) {
return false;
}
if (!Objects.equals(this.classParameterAnnotations, other.classParameterAnnotations)) {
return false;
}
if (!Objects.equals(this.runtimeParameterAnnotations, other.runtimeParameterAnnotations)) {
return false;
}
return true;
}
@ -3636,6 +3748,40 @@ public class CreateSymbols {
}
}
static final class TypeAnnotationDescription {
AnnotationDescription annotation;
Map<String, Object> targetInfo;
List<TypePathComponentDesc> typePath;
public TypeAnnotationDescription() {
}
public TypeAnnotationDescription(AnnotationDescription annotation, Map<String, Object> targetInfo, List<TypePathComponentDesc> typePath) {
this.annotation = annotation;
this.targetInfo = targetInfo;
this.typePath = typePath;
}
@Override
public String toString() {
return annotation.toString() + "{" + targetInfo.entrySet().stream().map(e -> e.getKey() + "=" + quote(printValue(e.getValue()), false)).collect(Collectors.joining(",")) + "}" +
(!typePath.isEmpty() ? "[" + typePath.stream().map(desc -> desc.tag + ":" + desc.index).collect(Collectors.joining(",")) + "]" : "");
}
private String printValue(Object obj) {
if (obj instanceof String s) {
return "\"" + s + "\"";
} else if (obj instanceof Integer i) {
return "I" + String.valueOf(i);
} else {
throw new IllegalStateException("Unsupported value: " + obj.getClass());
}
}
//TODO: path
record TypePathComponentDesc(String tag, int index) {}
}
static final class EnumConstant {
String type;
String constant;
@ -3975,23 +4121,69 @@ public class CreateSymbols {
private static AnnotationDescription parseAnnotation(String value, int[] valuePointer) {
String className = className(value, valuePointer);
Map<String, Object> attribute2Value = new HashMap<>();
Map<String, Object> attribute2Value = Map.of();
if (valuePointer[0] < value.length() && value.charAt(valuePointer[0]) == '(') {
while (value.charAt(valuePointer[0]) != ')') {
attribute2Value = parseMap(value, valuePointer, ')');
}
return new AnnotationDescription(className, attribute2Value);
}
private static Map<String, Object> parseMap(String value, int[] valuePointer, char endBracket) {
Map<String, Object> attribute2Value = new HashMap<>();
while (value.charAt(valuePointer[0]) != endBracket) {
int nameStart = ++valuePointer[0];
while (value.charAt(valuePointer[0]++) != '=');
String name = value.substring(nameStart, valuePointer[0] - 1);
attribute2Value.put(name, parseAnnotationValue(value, valuePointer));
}
valuePointer[0]++;
return attribute2Value;
}
public static List<TypeAnnotationDescription> parseTypeAnnotations(String encoded, int[] pointer) {
List<TypeAnnotationDescription> result = new ArrayList<>();
while (pointer[0] < encoded.length() && encoded.charAt(pointer[0]) == '@') {
pointer[0]++;
result.add(parseTypeAnnotation(encoded, pointer));
}
return result;
}
private static TypeAnnotationDescription parseTypeAnnotation(String value, int[] valuePointer) {
AnnotationDescription ann = parseAnnotation(value, valuePointer);
Map<String, Object> targetInfo = Map.of();
if (valuePointer[0] < value.length() && value.charAt(valuePointer[0]) == '{') {
targetInfo = parseMap(value, valuePointer, '}');
}
List<TypeAnnotationDescription.TypePathComponentDesc> typePath = new ArrayList<>();
if (valuePointer[0] < value.length() && value.charAt(valuePointer[0]) == '[') {
while (value.charAt(valuePointer[0]) != ']') {
int nameStart = ++valuePointer[0];
while (value.charAt(valuePointer[0]++) != '=');
while (value.charAt(valuePointer[0]++) != ':');
String name = value.substring(nameStart, valuePointer[0] - 1);
attribute2Value.put(name, parseAnnotationValue(value, valuePointer));
typePath.add(new TypeAnnotationDescription.TypePathComponentDesc(name, Integer.parseInt(readDigits(value, valuePointer))));
}
valuePointer[0]++;
}
return new AnnotationDescription(className, attribute2Value);
return new TypeAnnotationDescription(ann, targetInfo, typePath);
}
//</editor-fold>

View File

@ -399,6 +399,11 @@ public class Flags {
*/
public static final long RESTRICTED = 1L<<62; // MethodSymbols
/**
* Flag to indicate parameters that require identity.
*/
public static final long REQUIRES_IDENTITY = 1L<<62; // VarSymbols (parameters)
/**
* Flag to indicate type annotations have been queued for field initializers.
*/

View File

@ -969,7 +969,8 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
boolean isCurrentSymbolsAnnotation(Attribute.TypeCompound anno, int index) {
return (anno.position.type == TargetType.CLASS_TYPE_PARAMETER ||
anno.position.type == TargetType.METHOD_TYPE_PARAMETER) &&
anno.position.parameter_index == index;
anno.position.parameter_index == index &&
anno.type.tsym.flatName() != name.table.names.requiresIdentityInternal;
}

View File

@ -384,9 +384,15 @@ public class Symtab {
// Enter a synthetic class that is used to mark classes in ct.sym.
// This class does not have a class file.
private Type enterSyntheticAnnotation(String name) {
return enterSyntheticAnnotation(names.fromString(name));
}
// Enter a synthetic class that is used to mark classes in ct.sym.
// This class does not have a class file.
private Type enterSyntheticAnnotation(Name name) {
// for now, leave the module null, to prevent problems from synthesizing the
// existence of a class in any specific module, including noModule
ClassType type = (ClassType)enterClass(java_base, names.fromString(name)).type;
ClassType type = (ClassType)enterClass(java_base, name).type;
ClassSymbol sym = (ClassSymbol)type.tsym;
sym.completer = Completer.NULL_COMPLETER;
sym.flags_field = PUBLIC|ACYCLIC|ANNOTATION|INTERFACE;
@ -613,7 +619,7 @@ public class Symtab {
valueBasedType = enterClass("jdk.internal.ValueBased");
valueBasedInternalType = enterSyntheticAnnotation("jdk.internal.ValueBased+Annotation");
requiresIdentityType = enterClass("jdk.internal.RequiresIdentity");
requiresIdentityInternalType = enterSyntheticAnnotation("jdk.internal.RequiresIdentity+Annotation");
requiresIdentityInternalType = enterSyntheticAnnotation(names.requiresIdentityInternal);
classDescType = enterClass("java.lang.constant.ClassDesc");
enumDescType = enterClass("java.lang.Enum$EnumDesc");
// For serialization lint checking

View File

@ -384,6 +384,12 @@ public class Annotate {
&& types.isSameType(c.type, syms.restrictedType)) {
toAnnotate.flags_field |= Flags.RESTRICTED;
}
if (!c.type.isErroneous()
&& toAnnotate.kind == VAR
&& types.isSameType(c.type, syms.requiresIdentityType)) {
toAnnotate.flags_field |= Flags.REQUIRES_IDENTITY;
}
}
List<T> buf = List.nil();

View File

@ -5729,14 +5729,14 @@ public class Check {
if (!argExps.isEmpty() && msym instanceof MethodSymbol ms && ms.params != null) {
VarSymbol lastParam = ms.params.head;
for (VarSymbol param: ms.params) {
if (param.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) {
if ((param.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) {
lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected);
}
lastParam = param;
argExps = argExps.tail;
}
while (argExps != null && !argExps.isEmpty() && lastParam != null) {
if (lastParam.attribute(syms.requiresIdentityType.tsym) != null && argExps.head.type.isValueBased()) {
if ((lastParam.flags_field & REQUIRES_IDENTITY) != 0 && argExps.head.type.isValueBased()) {
lint.logIfEnabled(argExps.head.pos(), LintWarnings.AttemptToUseValueBasedWhereIdentityExpected);
}
argExps = argExps.tail;
@ -5813,7 +5813,7 @@ public class Check {
SymbolMetadata sm = t.tsym.getMetadata();
if (sm != null && !t.getTypeArguments().isEmpty()) {
if (sm.getTypeAttributes().stream()
.filter(ta -> ta.type.tsym == syms.requiresIdentityType.tsym &&
.filter(ta -> isRequiresIdentityAnnotation(ta.type.tsym) &&
t.getTypeArguments().get(ta.position.parameter_index) != null &&
t.getTypeArguments().get(ta.position.parameter_index).isValueBased()).findAny().isPresent()) {
requiresWarning = true;
@ -5838,11 +5838,16 @@ public class Check {
}
if (sm != null)
sm.getTypeAttributes().stream()
.filter(ta -> (ta.type.tsym == syms.requiresIdentityType.tsym) &&
.filter(ta -> isRequiresIdentityAnnotation(ta.type.tsym) &&
typeParamTrees.get(ta.position.parameter_index).type != null &&
typeParamTrees.get(ta.position.parameter_index).type.isValueBased())
.forEach(ta -> lint.logIfEnabled(typeParamTrees.get(ta.position.parameter_index).pos(),
CompilerProperties.LintWarnings.AttemptToUseValueBasedWhereIdentityExpected));
}
}
private boolean isRequiresIdentityAnnotation(TypeSymbol annoType) {
return annoType == syms.requiresIdentityType.tsym ||
annoType.flatName() == syms.requiresIdentityInternalType.tsym.flatName();
}
}

View File

@ -1556,6 +1556,9 @@ public class ClassReader {
} else if (proxy.type.tsym.flatName() == syms.restrictedInternalType.tsym.flatName()) {
Assert.check(sym.kind == MTH);
sym.flags_field |= RESTRICTED;
} else if (proxy.type.tsym.flatName() == syms.requiresIdentityInternalType.tsym.flatName()) {
Assert.check(sym.kind == VAR);
sym.flags_field |= REQUIRES_IDENTITY;
} else {
if (proxy.type.tsym == syms.annotationTargetType.tsym) {
target = proxy;
@ -1572,6 +1575,9 @@ public class ClassReader {
} else if (proxy.type.tsym == syms.restrictedType.tsym) {
Assert.check(sym.kind == MTH);
sym.flags_field |= RESTRICTED;
} else if (proxy.type.tsym == syms.requiresIdentityType.tsym) {
Assert.check(sym.kind == VAR);
sym.flags_field |= REQUIRES_IDENTITY;
}
proxies.append(proxy);
}
@ -2809,9 +2815,8 @@ public class ClassReader {
params.append(param);
if (parameterAnnotations != null) {
ParameterAnnotations annotations = parameterAnnotations[annotationIndex];
if (annotations != null && annotations.proxies != null
&& !annotations.proxies.isEmpty()) {
annotate.normal(new AnnotationCompleter(param, annotations.proxies));
if (annotations != null && annotations.proxies != null) {
attachAnnotations(param, annotations.proxies);
}
}
nameIndexLvt += Code.width(t);

View File

@ -228,6 +228,9 @@ public class Names {
public final Name enumSwitch;
public final Name enumConstant;
// special annotation names
public final Name requiresIdentityInternal;
public final Name.Table table;
@SuppressWarnings("this-escape")
@ -412,6 +415,9 @@ public class Names {
typeSwitch = fromString("typeSwitch");
enumSwitch = fromString("enumSwitch");
enumConstant = fromString("enumConstant");
// special annotations:
requiresIdentityInternal = fromString("jdk.internal.RequiresIdentity+Annotation");
}
protected Name.Table createTable(Options options) {

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2025, 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 8356894
* @summary Verify source level checks are performed properly
* @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 RequiresIdentityTest
*/
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import toolbox.TestRunner;
import toolbox.JavacTask;
import toolbox.Task;
import toolbox.ToolBox;
public class RequiresIdentityTest extends TestRunner {
ToolBox tb;
public static void main(String... args) throws Exception {
new RequiresIdentityTest().runTests();
}
RequiresIdentityTest() {
super(System.err);
tb = new ToolBox();
}
public void runTests() throws Exception {
runTests(m -> new Object[] { Paths.get(m.getName()) });
}
@Test
public void testReleaseWorksAsCurrentVersion(Path base) throws Exception {
Path src = base.resolve("src");
Path classes = base.resolve("classes");
tb.writeJavaFiles(src,
"""
import java.util.WeakHashMap;
import java.util.Optional;
public class Test {
void test() {
WeakHashMap<Optional<Integer>, Object> m = null;
m.put(Optional.empty(), 1);
}
}
""");
Files.createDirectories(classes);
var expectedErrors = List.of(
"Test.java:6:20: compiler.warn.attempt.to.use.value.based.where.identity.expected",
"Test.java:7:29: compiler.warn.attempt.to.use.value.based.where.identity.expected",
"2 warnings"
);
{
var actualErrors =
new JavacTask(tb)
.options("-XDrawDiagnostics")
.outdir(classes)
.files(tb.findJavaFiles(src))
.run()
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
if (!expectedErrors.equals(actualErrors)) {
throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) +
", actual: " + actualErrors);
}
}
{
var actualErrors =
new JavacTask(tb)
.options("--release", System.getProperty("java.specification.version"),
"-XDrawDiagnostics")
.outdir(classes)
.files(tb.findJavaFiles(src))
.run()
.writeAll()
.getOutputLines(Task.OutputKind.DIRECT);
if (!expectedErrors.equals(actualErrors)) {
throw new AssertionError("Incorrect errors, expected: " + List.of(expectedErrors) +
", actual: " + actualErrors);
}
}
}
@Test
public void testModel(Path base) throws Exception {
{
List<String> printed =
new JavacTask(tb)
.options("-Xprint")
.classes("java.util.WeakHashMap")
.run()
.writeAll()
.getOutputLines(Task.OutputKind.STDOUT);
printed.removeIf(l -> !l.contains("put(") && !l.contains("class WeakHashMap<"));
List<String> expected = List.of(
"public class WeakHashMap<@jdk.internal.RequiresIdentity K, V> extends java.util.AbstractMap<K,V> implements java.util.Map<K,V> {",
" public V put(@jdk.internal.RequiresIdentity sealed K key,"
);
if (!expected.equals(printed)) {
throw new AssertionError("Expected: " + expected +
", but got: " + printed);
}
}
{
List<String> printed =
new JavacTask(tb)
.options("--release", System.getProperty("java.specification.version"),
"-Xprint")
.classes("java.util.WeakHashMap")
.run()
.writeAll()
.getOutputLines(Task.OutputKind.STDOUT);
printed.removeIf(l -> !l.contains("put(") && !l.contains("class WeakHashMap<"));
List<String> expected = List.of(
"public class WeakHashMap<K, V> extends java.util.AbstractMap<K,V> implements java.util.Map<K,V> {",
" public V put(sealed K arg0,"
);
if (!expected.equals(printed)) {
throw new AssertionError("Expected: " + expected +
", but got: " + printed);
}
}
}
}

View File

@ -0,0 +1,4 @@
RequiresIdentityTest.java:16:18: compiler.warn.attempt.to.use.value.based.where.identity.expected
- compiler.err.warnings.and.werror
1 error
1 warning

View File

@ -21,19 +21,6 @@
* questions.
*/
/**
* @test
* @bug 8072480 8277106 8331027
* @summary Unit test for CreateSymbols
* @modules java.compiler
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.jvm
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.util
* @clean *
* @run main/othervm CreateSymbolsTest
*/
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, 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
@ -21,6 +21,19 @@
* questions.
*/
/**
* @test
* @bug 8072480 8277106 8331027
* @summary Unit test for CreateSymbols
* @modules java.compiler
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.jvm
* jdk.compiler/com.sun.tools.javac.main
* jdk.compiler/com.sun.tools.javac.util
* @clean *
* @run main/othervm CreateSymbolsTest
*/
import java.io.File;
import java.io.InputStream;
import java.io.Writer;
@ -908,6 +921,109 @@ public class CreateSymbolsTestImpl {
""");
}
@Test
void testTypeAnnotations() throws Exception {
doPrintElementTest("""
package t;
public class T {
}
""",
"""
package t;
import java.lang.annotation.*;
import java.util.*;
public class T<@AnnInvisible @AnnVisible E extends @AnnInvisible @AnnVisible ArrayList<@AnnInvisible @AnnVisible ArrayList>> extends @AnnInvisible @AnnVisible ArrayList {
public @AnnInvisible @AnnVisible List<@AnnInvisible @AnnVisible E> field;
public <@AnnInvisible @AnnVisible M extends @AnnInvisible @AnnVisible ArrayList<@AnnInvisible @AnnVisible ArrayList>> @AnnInvisible @AnnVisible List<@AnnInvisible @AnnVisible M> convert(@AnnInvisible @AnnVisible T<E> this, @AnnInvisible @AnnVisible M e1, @AnnInvisible @AnnVisible List<@AnnInvisible @AnnVisible E> e2) throws @AnnInvisible @AnnVisible IllegalStateException, @AnnInvisible @AnnVisible IllegalArgumentException {
return null;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface AnnVisible {
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE_USE)
@interface AnnInvisible {
}
""",
"t.T",
"""
package t;
public class T {
public T();
}
""",
"t.T",
"""
package t;
public class T<@t.AnnInvisible @t.AnnVisible E extends java.util.@t.AnnInvisible @t.AnnVisible ArrayList<java.util.@t.AnnInvisible @t.AnnVisible ArrayList>> extends java.util.@t.AnnInvisible @t.AnnVisible ArrayList {
public java.util.@t.AnnInvisible @t.AnnVisible List<@t.AnnInvisible @t.AnnVisible E> field;
public T();
public <@t.AnnInvisible @t.AnnVisible M extends java.util.@t.AnnInvisible @t.AnnVisible ArrayList<java.util.@t.AnnInvisible @t.AnnVisible ArrayList>> java.util.@t.AnnInvisible @t.AnnVisible List<@t.AnnInvisible @t.AnnVisible M> convert(@t.AnnInvisible @t.AnnVisible M arg0,
java.util.@t.AnnInvisible @t.AnnVisible List<@t.AnnInvisible @t.AnnVisible E> arg1) throws java.lang.@t.AnnInvisible @t.AnnVisible IllegalStateException,\s
java.lang.@t.AnnInvisible @t.AnnVisible IllegalArgumentException;
}
""");
}
@Test
void testParameterAnnotations() throws Exception {
doPrintElementTest("""
package t;
public class T {
public void test(int p1, int p2) {
}
}
""",
"""
package t;
import java.lang.annotation.*;
import java.util.*;
public class T {
public void test(@AnnVisible int p1, @AnnInvisible int p2) {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface AnnVisible {
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.PARAMETER)
@interface AnnInvisible {
}
""",
"t.T",
"""
package t;
public class T {
public T();
public void test(int arg0,
int arg1);
}
""",
"t.T",
"""
package t;
public class T {
public T();
public void test(@t.AnnVisible int arg0,
@t.AnnInvisible int arg1);
}
""");
}
void doTestData(String data,
String... code) throws Exception {
String testClasses = System.getProperty("test.classes");