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