1 /*
2 * Copyright (c) 2023, 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 8308590
27 * @summary Test basic modeling for value classes
28 * @library /tools/lib /tools/javac/lib
29 * @modules
30 * jdk.compiler/com.sun.tools.javac.api
31 * jdk.compiler/com.sun.tools.javac.main
32 * @build toolbox.ToolBox toolbox.JavacTask JavacTestingAbstractProcessor
33 * @run main TestValueClasses
34 */
35
36 import java.io.*;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.util.*;
41
42 import javax.annotation.processing.*;
43 import javax.lang.model.*;
44 import javax.lang.model.element.*;
45 import javax.lang.model.type.*;
46 import javax.lang.model.util.*;
47 import java.time.*;
48
49 import toolbox.JavacTask;
50 import toolbox.Task;
51 import toolbox.Task.Mode;
52 import toolbox.Task.OutputKind;
53 import toolbox.TestRunner;
54 import toolbox.ToolBox;
55
56 public class TestValueClasses extends TestRunner {
57
58 protected ToolBox tb;
59
60 TestValueClasses() {
61 super(System.err);
62 tb = new ToolBox();
63 }
64
65 public static void main(String... args) throws Exception {
66 new TestValueClasses().runTests();
67 }
68
69 /**
70 * Run all methods annotated with @Test, and throw an exception if any
71 * errors are reported..
72 *
73 * @throws Exception if any errors occurred
74 */
75 protected void runTests() throws Exception {
76 runTests(m -> new Object[] { Paths.get(m.getName()) });
77 }
78
79 Path[] findJavaFiles(Path... paths) throws IOException {
80 return tb.findJavaFiles(paths);
81 }
82
83 void checkOutputContains(String log, String... expect) throws Exception {
84 for (String e : expect) {
85 if (!log.contains(e)) {
86 throw new Exception("expected output not found: " + e);
87 }
88 }
89 }
90
91 @Test
92 public void testValueClassesProcessor(Path base) throws Exception {
93 Path src = base.resolve("src");
94 Path r = src.resolve("Test");
95
96 Path classes = base.resolve("classes");
97
98 Files.createDirectories(classes);
99
100 tb.writeJavaFiles(r,
101 """
102 interface Interface {}
103
104 value class ValueClass {}
105
106 class IdentityClass {}
107
108 value record ValueRecord() {}
109 """
110 );
111
112 List<String> expected = List.of(
113 "- compiler.note.proc.messager: visiting: Interface Modifiers: [abstract]",
114 "- compiler.note.proc.messager: visiting: ValueClass Modifiers: [value, final]",
115 "- compiler.note.proc.messager: constructor modifiers: []",
116 "- compiler.note.proc.messager: visiting: IdentityClass Modifiers: []",
117 "- compiler.note.proc.messager: constructor modifiers: []",
118 "- compiler.note.proc.messager: visiting: ValueRecord Modifiers: [value, final]",
119 "- compiler.note.proc.messager: constructor modifiers: []",
120 "- compiler.note.preview.filename: Interface.java, DEFAULT",
121 "- compiler.note.preview.recompile"
122 );
123
124 for (Mode mode : new Mode[] {Mode.API}) {
125 List<String> log = new JavacTask(tb, mode)
126 .options("--enable-preview", "-source", String.valueOf(Runtime.version().feature()), "-processor", ValueClassesProcessor.class.getName(),
127 "-XDrawDiagnostics")
128 .files(findJavaFiles(src))
129 .outdir(classes)
130 .run()
131 .writeAll()
132 .getOutputLines(Task.OutputKind.DIRECT);
133
134 System.out.println("log:" +log);
135
136 if (!expected.equals(log)) {
137 if (expected.size() == log.size()) {
138 for (int i = 0; i < expected.size(); i++) {
139 if (!expected.get(i).equals(log.get(i))) {
140 System.err.println("failing at line " + (i + 1));
141 System.err.println(" expecting " + expected.get(i));
142 System.err.println(" found " + log.get(i));
143 }
144 }
145 } else {
146 System.err.println("expected and log lists differ in length");
147 }
148 throw new AssertionError("Unexpected output: " + log);
149 }
150 }
151 }
152
153 public static final class ValueClassesProcessor extends JavacTestingAbstractProcessor {
154
155 @Override
156 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
157 if (!roundEnv.processingOver()) {
158 Messager messager = processingEnv.getMessager();
159 ElementScanner scanner = new ValueClassesScanner(messager);
160 for(Element rootElement : roundEnv.getRootElements()) {
161 scanner.visit(rootElement);
162 }
163 }
164 return true;
165 }
166
167 class ValueClassesScanner extends ElementScanner<Void, Void> {
168
169 Messager messager;
170
171 public ValueClassesScanner(Messager messager) {
172 this.messager = messager;
173 }
174
175 @Override
176 public Void visitType(TypeElement element, Void p) {
177 messager.printNote("visiting: " + element.getSimpleName() + " Modifiers: " + element.getModifiers());
178 List<? extends Element> enclosedElements = element.getEnclosedElements();
179 for (Element elem : enclosedElements) {
180 System.out.println("visiting " + elem.getSimpleName());
181 switch (elem.getSimpleName().toString()) {
182 case "<init>":
183 messager.printNote(" constructor modifiers: " + elem.getModifiers());
184 break;
185 default:
186 break;
187 }
188 }
189 return super.visitType(element, p);
190 }
191 }
192 }
193 }