1 /*
2 * Copyright (c) 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 * @modules jdk.incubator.code
27 * @enablePreview
28 * @library ../lib
29 * @modules jdk.compiler/com.sun.tools.javac.api
30 * @summary Smoke test for accessing IR from annotation processors
31 * @run main TestIRFromAnnotation
32 */
33
34 import com.sun.source.util.JavacTask;
35
36 import java.io.File;
37 import java.io.IOException;
38 import java.io.StringWriter;
39 import jdk.incubator.code.Op;
40 import jdk.incubator.code.dialect.core.CoreOp.FuncOp;
41 import jdk.incubator.code.dialect.java.JavaOp;
42 import jdk.incubator.code.extern.OpParser;
43 import jdk.incubator.code.extern.OpWriter;
44 import java.nio.charset.Charset;
45 import java.nio.file.FileVisitResult;
46 import java.nio.file.Files;
47 import java.nio.file.Path;
48 import java.nio.file.SimpleFileVisitor;
49 import java.nio.file.attribute.BasicFileAttributes;
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Optional;
53 import java.util.Set;
54 import javax.annotation.processing.RoundEnvironment;
55 import javax.lang.model.SourceVersion;
56 import javax.lang.model.element.*;
57 import javax.tools.JavaCompiler;
58 import javax.tools.JavaFileManager;
59 import javax.tools.SimpleJavaFileObject;
60 import javax.tools.ToolProvider;
61
62 public class TestIRFromAnnotation {
63
64 static final Set<String> EXCLUDED_TEST = Set.of(
65 "SuperTest.java", // @@@ Might be issue referencing auxiliary interface
66 // in method superInterfaceMethodInvocation
67 "LocalClassTest.java", // name of local classes is not stable at annotation processing time
68 "TestLocalCapture.java", // plain junit test
69 "TestCaptureQuoted.java", // plain junit test
70 "TestCaptureQuotable.java", // plain junit test
71 "QuotedSameInstanceTest.java", // plain junit test
72 "CodeModelSameInstanceTest.java" // plain junit test
73 );
74
75 public static void main(String... args) throws Exception {
76 String testSrc = System.getProperty("test.src");
77 File baseDir = Path.of(testSrc).toFile();
78 new TestIRFromAnnotation().run(baseDir);
79 }
80
81 void run(File baseDir) throws Exception {
82 for (File file : getAllFiles(List.of(baseDir))) {
83 if (!file.exists() || !file.getName().endsWith(".java") || isExcluded(file)) {
84 continue;
85 }
86 analyze(file);
87 }
88 }
89
90 boolean isExcluded(File file) {
91 return EXCLUDED_TEST.contains(file.getName());
92 }
93
94 void analyze(File source) {
95 try {
96 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
97 JavaFileManager fileManager = compiler.getStandardFileManager(null, null, Charset.defaultCharset());
98 JavacTask task = (JavacTask)compiler.getTask(null, fileManager, null,
99 List.of("-proc:only",
100 "--enable-preview",
101 "--add-modules=jdk.incubator.code",
102 "--source", Integer.toString(SourceVersion.latest().runtimeVersion().feature())),
103 null, List.of(new SourceFile(source)));
104 task.setProcessors(List.of(new Processor()));
105 task.analyze();
106 } catch (Throwable ex) {
107 throw new AssertionError("Unexpected exception when analyzing: " + source, ex);
108 }
109 }
110
111 File[] getAllFiles(List<File> roots) throws IOException {
112 long now = System.currentTimeMillis();
113 ArrayList<File> buf = new ArrayList<>();
114 for (File file : roots) {
115 Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
116 @Override
117 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
118 buf.add(file.toFile());
119 return FileVisitResult.CONTINUE;
120 }
121 });
122 }
123 long delta = System.currentTimeMillis() - now;
124 System.err.println("All files = " + buf.size() + " " + delta);
125 return buf.toArray(new File[buf.size()]);
126 }
127
128 static class SourceFile extends SimpleJavaFileObject {
129
130 private final File file;
131 protected SourceFile(File file) {
132 super(file.toURI(), Kind.SOURCE);
133 this.file = file;
134 }
135
136 @Override
137 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
138 return Files.readString(file.toPath());
139 }
140 }
141
142 public static class Processor extends JavacTestingAbstractProcessor {
143
144 public boolean process(Set<? extends TypeElement> annotations,
145 RoundEnvironment roundEnvironment) {
146 class Scan extends ElementScanner<Void,Void> {
147 @Override
148 public Void visitExecutable(ExecutableElement e, Void p) {
149 IR ir = e.getAnnotation(IR.class);
150 if (ir == null) {
151 return null; // skip
152 }
153 Optional<FuncOp> body = Op.ofElement(processingEnv, e);
154 if (!body.isPresent()) {
155 throw new AssertionError(String.format("No body found in method %s annotated with @IR",
156 toMethodString(e)));
157 }
158 String actualOp = canonicalizeModel((FuncOp)body.get());
159 String expectedOp = canonicalizeModel(ir.value());
160 if (!actualOp.equals(expectedOp)) {
161 throw new AssertionError(String.format("Bad IR found in %s:\n%s\nExpected:\n%s",
162 toMethodString(e), actualOp, expectedOp));
163 }
164 return null;
165 }
166 }
167 Scan scan = new Scan();
168 for (Element e : roundEnvironment.getRootElements()) {
169 scan.scan(e);
170 }
171 return true;
172 }
173 }
174
175 // serializes dropping location information, parses, and then serializes, dropping location information
176 static String canonicalizeModel(Op o) {
177 return canonicalizeModel(serialize(o));
178 }
179
180 // parses, and then serializes, dropping location information
181 static String canonicalizeModel(String d) {
182 return serialize(OpParser.fromString(JavaOp.JAVA_DIALECT_FACTORY, d).get(0));
183 }
184
185 // serializes, dropping location information
186 static String serialize(Op o) {
187 StringWriter w = new StringWriter();
188 OpWriter.writeTo(w, o, OpWriter.LocationOption.DROP_LOCATION);
189 return w.toString();
190 }
191
192 static String toMethodString(ExecutableElement e) {
193 return e.getEnclosingElement() + "." + e;
194 }
195
196 }