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         );
109 
110         List<String> expected = List.of(
111                 "- compiler.note.proc.messager: visiting: Interface Modifiers: [abstract]",
112                 "- compiler.note.proc.messager: visiting: ValueClass Modifiers: [value, final]",
113                 "- compiler.note.proc.messager:     constructor modifiers: []",
114                 "- compiler.note.proc.messager: visiting: IdentityClass Modifiers: []",
115                 "- compiler.note.proc.messager:     constructor modifiers: []",
116                 "- compiler.note.preview.filename: Interface.java, DEFAULT",
117                 "- compiler.note.preview.recompile"
118         );
119 
120         for (Mode mode : new Mode[] {Mode.API}) {
121             List<String> log = new JavacTask(tb, mode)
122                     .options("--enable-preview", "-source", String.valueOf(Runtime.version().feature()), "-processor", ValueClassesProcessor.class.getName(),
123                             "-XDrawDiagnostics")
124                     .files(findJavaFiles(src))
125                     .outdir(classes)
126                     .run()
127                     .writeAll()
128                     .getOutputLines(Task.OutputKind.DIRECT);
129 
130             System.out.println("log:" +log);
131 
132             if (!expected.equals(log)) {
133                 if (expected.size() == log.size()) {
134                     for (int i = 0; i < expected.size(); i++) {
135                         if (!expected.get(i).equals(log.get(i))) {
136                             System.err.println("failing at line " + (i + 1));
137                             System.err.println("    expecting " + expected.get(i));
138                             System.err.println("    found " + log.get(i));
139                         }
140                     }
141                 } else {
142                     System.err.println("expected and log lists differ in length");
143                 }
144                 throw new AssertionError("Unexpected output: " + log);
145             }
146         }
147     }
148 
149     public static final class ValueClassesProcessor extends JavacTestingAbstractProcessor {
150 
151         @Override
152         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
153             if (!roundEnv.processingOver()) {
154                 Messager messager = processingEnv.getMessager();
155                 ElementScanner scanner = new ValueClassesScanner(messager);
156                 for(Element rootElement : roundEnv.getRootElements()) {
157                     scanner.visit(rootElement);
158                 }
159             }
160             return true;
161         }
162 
163         class ValueClassesScanner extends ElementScanner<Void, Void> {
164 
165             Messager messager;
166 
167             public ValueClassesScanner(Messager messager) {
168                 this.messager = messager;
169             }
170 
171             @Override
172             public Void visitType(TypeElement element, Void p) {
173                 messager.printNote("visiting: " + element.getSimpleName() + " Modifiers: " + element.getModifiers());
174                 List<? extends Element> enclosedElements = element.getEnclosedElements();
175                 for (Element elem : enclosedElements) {
176                     System.out.println("visiting " + elem.getSimpleName());
177                     switch (elem.getSimpleName().toString()) {
178                         case "<init>":
179                             messager.printNote("    constructor modifiers: " + elem.getModifiers());
180                             break;
181                         default:
182                             break;
183                     }
184                 }
185                 return super.visitType(element, p);
186             }
187         }
188     }
189 }