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.op.CoreOp.FuncOp; 41 import jdk.incubator.code.op.ExtendedOp; 42 import jdk.incubator.code.parser.OpParser; 43 import jdk.incubator.code.writer.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 testng test 69 "TestCaptureQuoted.java", // plain testng test 70 "TestCaptureQuotable.java", // plain testng test 71 "QuotedSameInstanceTest.java", // plain testng test 72 "CodeModelSameInstanceTest.java" // plain testng 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(ExtendedOp.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 }