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 "LocalClassTest.java" // name of local classes is not stable at annotation processing time
66 );
67
68 public static void main(String... args) throws Exception {
69 String testSrc = System.getProperty("test.src");
70 File baseDir = Path.of(testSrc).toFile();
71 new TestIRFromAnnotation().run(baseDir);
72 }
73
74 void run(File baseDir) throws Exception {
75 for (File file : getAllFiles(List.of(baseDir))) {
76 if (!file.exists() || !file.getName().endsWith(".java") || isExcluded(file)) {
77 continue;
78 }
79 analyze(file);
80 }
81 }
82
83 boolean isExcluded(File file) {
84 return EXCLUDED_TEST.contains(file.getName());
85 }
86
87 void analyze(File source) {
88 try {
89 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
90 JavaFileManager fileManager = compiler.getStandardFileManager(null, null, Charset.defaultCharset());
91 JavacTask task = (JavacTask)compiler.getTask(null, fileManager, null,
92 List.of("-proc:only",
93 "--enable-preview",
94 "--add-modules=jdk.incubator.code",
95 "--source", Integer.toString(SourceVersion.latest().runtimeVersion().feature())),
96 null, List.of(new SourceFile(source)));
97 task.setProcessors(List.of(new Processor()));
98 task.analyze();
99 } catch (Throwable ex) {
100 throw new AssertionError("Unexpected exception when analyzing: " + source, ex);
101 }
102 }
103
104 File[] getAllFiles(List<File> roots) throws IOException {
105 long now = System.currentTimeMillis();
106 ArrayList<File> buf = new ArrayList<>();
107 for (File file : roots) {
108 Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
109 @Override
110 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
111 buf.add(file.toFile());
112 return FileVisitResult.CONTINUE;
113 }
114 });
115 }
116 long delta = System.currentTimeMillis() - now;
117 System.err.println("All files = " + buf.size() + " " + delta);
118 return buf.toArray(new File[buf.size()]);
119 }
120
121 static class SourceFile extends SimpleJavaFileObject {
122
123 private final File file;
124 protected SourceFile(File file) {
125 super(file.toURI(), Kind.SOURCE);
126 this.file = file;
127 }
128
129 @Override
130 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
131 return Files.readString(file.toPath());
132 }
133 }
134
135 public static class Processor extends JavacTestingAbstractProcessor {
136
137 public boolean process(Set<? extends TypeElement> annotations,
138 RoundEnvironment roundEnvironment) {
139 class Scan extends ElementScanner<Void,Void> {
140 @Override
141 public Void visitExecutable(ExecutableElement e, Void p) {
142 IR ir = e.getAnnotation(IR.class);
143 if (ir == null) {
144 return null; // skip
145 }
146 Optional<FuncOp> body = Op.ofElement(processingEnv, e);
147 if (!body.isPresent()) {
148 throw new AssertionError(String.format("No body found in method %s annotated with @IR",
149 toMethodString(e)));
150 }
151 String actualOp = canonicalizeModel((FuncOp)body.get());
152 String expectedOp = canonicalizeModel(ir.value());
153 if (!actualOp.equals(expectedOp)) {
154 throw new AssertionError(String.format("Bad IR found in %s:\n%s\nExpected:\n%s",
155 toMethodString(e), actualOp, expectedOp));
156 }
157 return null;
158 }
159 }
160 Scan scan = new Scan();
161 for (Element e : roundEnvironment.getRootElements()) {
162 scan.scan(e);
163 }
164 return true;
165 }
166 }
167
168 // serializes dropping location information, parses, and then serializes, dropping location information
169 static String canonicalizeModel(Op o) {
170 return canonicalizeModel(serialize(o));
171 }
172
173 // parses, and then serializes, dropping location information
174 static String canonicalizeModel(String d) {
175 return serialize(OpParser.fromText(JavaOp.JAVA_DIALECT_FACTORY, d).get(0));
176 }
177
178 // serializes, dropping location information
179 static String serialize(Op o) {
180 StringWriter w = new StringWriter();
181 OpWriter.writeTo(w, o, OpWriter.LocationOption.DROP_LOCATION);
182 return w.toString();
183 }
184
185 static String toMethodString(ExecutableElement e) {
186 return e.getEnclosingElement() + "." + e;
187 }
188
189 }