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 }