1 /* 2 * Copyright (c) 2021, 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 8272234 27 * @summary Verify proper handling of originating elements in javac's Filer. 28 * @library /tools/lib 29 * @modules jdk.compiler/com.sun.tools.javac.api 30 * jdk.compiler/com.sun.tools.javac.main 31 * @build toolbox.TestRunner toolbox.ToolBox TestOriginatingElements 32 * @run main TestOriginatingElements 33 */ 34 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.util.List; 39 import java.util.Set; 40 41 import javax.annotation.processing.AbstractProcessor; 42 import javax.annotation.processing.RoundEnvironment; 43 import javax.annotation.processing.SupportedAnnotationTypes; 44 import javax.lang.model.SourceVersion; 45 import javax.lang.model.element.TypeElement; 46 import javax.tools.JavaCompiler; 47 import javax.tools.JavaFileObject; 48 import javax.tools.StandardJavaFileManager; 49 import javax.tools.ToolProvider; 50 51 import java.io.IOException; 52 import java.io.OutputStream; 53 import java.net.URI; 54 import java.util.ArrayList; 55 import java.util.Base64; 56 import java.util.Iterator; 57 import java.util.stream.Stream; 58 import javax.annotation.processing.Filer; 59 import javax.annotation.processing.SupportedOptions; 60 import javax.lang.model.element.Element; 61 import javax.lang.model.element.ModuleElement; 62 import javax.lang.model.element.PackageElement; 63 import javax.lang.model.util.Elements; 64 import javax.tools.FileObject; 65 import javax.tools.ForwardingJavaFileManager; 66 import javax.tools.JavaFileManager; 67 import javax.tools.SimpleJavaFileObject; 68 import javax.tools.StandardLocation; 69 import toolbox.JavacTask; 70 import toolbox.TestRunner; 71 import toolbox.ToolBox; 72 import toolbox.ToolBox.MemoryFileManager; 73 74 public class TestOriginatingElements extends TestRunner { 75 76 public static void main(String... args) throws Exception { 77 new TestOriginatingElements().runTests(m -> new Object[] { Paths.get(m.getName()) }); 78 } 79 80 private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 81 private final ToolBox tb = new ToolBox(); 82 83 public TestOriginatingElements() { 84 super(System.err); 85 } 86 87 @Test 88 public void testOriginatingElements(Path outerBase) throws Exception { 89 Path libSrc = outerBase.resolve("lib-src"); 90 tb.writeJavaFiles(libSrc, 91 """ 92 module lib { exports lib1; exports lib2; } 93 """, 94 """ 95 package lib1; 96 public @interface A { 97 } 98 """, 99 """ 100 package lib2; 101 public class Lib { 102 } 103 """); 104 tb.writeFile(libSrc.resolve("lib1/package-info.java"), "@A package lib1;"); 105 Path libClasses = outerBase.resolve("lib-classes"); 106 Path libClassesModule = libClasses.resolve("lib"); 107 Files.createDirectories(libClassesModule); 108 109 new JavacTask(tb) 110 .files(tb.findJavaFiles(libSrc)) 111 .outdir(libClassesModule) 112 .run(); 113 114 Path src = outerBase.resolve("src"); 115 tb.writeJavaFiles(src, 116 """ 117 module m {} 118 """, 119 """ 120 package t; 121 public class T1 { 122 } 123 """, 124 """ 125 package t; 126 public class T2 { 127 } 128 """, 129 """ 130 package t; 131 public class T3 { 132 } 133 """); 134 tb.writeFile(src.resolve("p/package-info.java"), "package p;"); 135 Path classes = outerBase.resolve("classes"); 136 Files.createDirectories(classes); 137 List<String> testOutput = new ArrayList<>(); 138 try (StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null); 139 JavaFileManager fm = capturingFileManager(sjfm, testOutput)) { 140 String generatedData; 141 try (MemoryFileManager mfm = new MemoryFileManager(sjfm, /* shouldClose */ false)) { 142 compiler.getTask(null, mfm, null, null, null, 143 List.of(new ToolBox.JavaSource("package test; public class Generated2 {}"))) 144 .call(); 145 generatedData = 146 Base64.getEncoder().encodeToString(mfm.getFileBytes(StandardLocation.CLASS_OUTPUT, "test.Generated2")); 147 } 148 List<String> options = List.of("-sourcepath", src.toString(), 149 "-processor", "TestOriginatingElements$P", 150 "-processorpath", System.getProperty("test.class.path"), 151 "--module-path", libClasses.toString(), 152 "--add-modules", "lib", 153 "-d", classes.toString(), 154 "-AgeneratedData=" + generatedData); 155 ToolProvider.getSystemJavaCompiler() 156 .getTask(null, fm, null, options, null, sjfm.getJavaFileObjects(tb.findJavaFiles(src))) 157 .call(); 158 } catch (IOException ex) { 159 throw new IllegalStateException(ex); 160 } 161 List<String> expectedOriginatingFiles = List.of("t.T1(SOURCE)", 162 "java.lang.String(CLASS)", 163 "p.package-info(SOURCE)", 164 "lib1.package-info(CLASS)", 165 "module-info(SOURCE)", 166 "module-info(CLASS)", 167 "t.T2(SOURCE)", 168 "java.lang.CharSequence(CLASS)", 169 "p.package-info(SOURCE)", 170 "lib1.package-info(CLASS)", 171 "module-info(SOURCE)", 172 "module-info(CLASS)", 173 "t.T3(SOURCE)", 174 "java.lang.Exception(CLASS)", 175 "p.package-info(SOURCE)", 176 "lib1.package-info(CLASS)", 177 "module-info(SOURCE)", 178 "module-info(CLASS)"); 179 assertEquals(expectedOriginatingFiles, testOutput); 180 } 181 182 static JavaFileManager capturingFileManager(JavaFileManager sjfm, List<String> testOutput) { 183 return new ForwardingJavaFileManager<JavaFileManager>(sjfm) { 184 @Override 185 public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location, 186 String className, 187 JavaFileObject.Kind kind, 188 FileObject... originatingFiles) throws IOException { 189 Stream.of(originatingFiles) 190 .map(this::getInfo) 191 .forEach(testOutput::add); 192 return super.getJavaFileForOutputForOriginatingFiles(location, className, kind, originatingFiles); 193 } 194 195 @Override 196 public FileObject getFileForOutputForOriginatingFiles(Location location, 197 String packageName, 198 String relativeName, 199 FileObject... originatingFiles) throws IOException { 200 Stream.of(originatingFiles) 201 .map(this::getInfo) 202 .forEach(testOutput::add); 203 return super.getFileForOutputForOriginatingFiles(location, packageName, relativeName, originatingFiles); 204 } 205 206 private String getInfo(FileObject fo) { 207 try { 208 JavaFileObject jfo = (JavaFileObject) fo; //the test only expects JavaFileObjects here: 209 JavaFileManager.Location location = jfo.getKind() == JavaFileObject.Kind.SOURCE 210 ? StandardLocation.SOURCE_PATH 211 : sjfm.getLocationForModule(StandardLocation.SYSTEM_MODULES, "java.base"); 212 String binaryName = inferBinaryName(location, jfo); 213 return binaryName + "(" + jfo.getKind() + ")"; 214 } catch (IOException ex) { 215 throw new AssertionError(ex); 216 } 217 } 218 219 @Override 220 public void close() { 221 // Don't close the delegate. 222 } 223 }; 224 } 225 226 @SupportedAnnotationTypes("*") 227 @SupportedOptions("generatedData") 228 public static class P extends AbstractProcessor { 229 int round; 230 @Override 231 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 232 if (round++ == 0) { 233 Elements elems = processingEnv.getElementUtils(); 234 ModuleElement mdl = elems.getModuleElement("m"); 235 ModuleElement java_base = elems.getModuleElement("java.base"); 236 PackageElement pack = elems.getPackageElement("p"); 237 PackageElement lib1Pack = elems.getPackageElement("lib1"); 238 PackageElement lib2Pack = elems.getPackageElement("lib2"); 239 Filer filer = processingEnv.getFiler(); 240 try { 241 filer.createSourceFile("test.Generated1", 242 element("t.T1"), 243 element("java.lang.String"), 244 pack, 245 lib1Pack, 246 lib2Pack, 247 mdl, 248 java_base).openOutputStream().close(); 249 try (OutputStream out = filer.createClassFile("test.Generated2", 250 element("t.T2"), 251 element("java.lang.CharSequence"), 252 pack, 253 lib1Pack, 254 lib2Pack, 255 mdl, 256 java_base).openOutputStream()) { 257 out.write(Base64.getDecoder().decode(processingEnv.getOptions().get("generatedData"))); 258 } 259 filer.createResource(StandardLocation.CLASS_OUTPUT, 260 "test", 261 "Generated3.txt", 262 element("t.T3"), 263 element("java.lang.Exception"), 264 pack, 265 lib1Pack, 266 lib2Pack, 267 mdl, 268 java_base).openOutputStream().close(); 269 } catch (IOException ex) { 270 throw new AssertionError(ex); 271 } 272 } 273 return false; 274 } 275 276 private Element element(String type) { 277 return processingEnv.getElementUtils().getTypeElement(type); 278 } 279 280 @Override 281 public SourceVersion getSupportedSourceVersion() { 282 return SourceVersion.latest(); 283 } 284 } 285 286 @Test 287 public void testVacuousJavaFileManager(Path outerBase) throws Exception { 288 List<String> log = new ArrayList<>(); 289 JavaFileObject expectedOut = new SimpleJavaFileObject(new URI("Out.java"), JavaFileObject.Kind.SOURCE) {}; 290 JavaFileManager fm = new MinimalJavaFileManager() { 291 @Override 292 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 293 log.add("getJavaFileForOutput(" + location + ", " + className + ", " + kind + ", " + sibling); 294 return expectedOut; 295 } 296 @Override 297 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 298 log.add("getFileForOutput(" + location + ", " + packageName + ", " + relativeName + ", " + sibling); 299 return expectedOut; 300 } 301 }; 302 303 FileObject fo1 = new SimpleJavaFileObject(new URI("Test1.java"), JavaFileObject.Kind.SOURCE) { 304 @Override 305 public String toString() { 306 return "Test1 - FO"; 307 } 308 }; 309 FileObject fo2 = new SimpleJavaFileObject(new URI("Test2.java"), JavaFileObject.Kind.SOURCE) { 310 @Override 311 public String toString() { 312 return "Test2 - FO"; 313 } 314 }; 315 316 assertEquals(expectedOut, 317 fm.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2)); 318 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, Test1 - FO"), log); log.clear(); 319 320 assertEquals(expectedOut, 321 fm.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE)); 322 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, null"), log); log.clear(); 323 324 assertEquals(expectedOut, 325 fm.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2)); 326 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, Test1 - FO"), log); log.clear(); 327 assertEquals(expectedOut, 328 fm.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java")); 329 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, null"), log); log.clear(); 330 } 331 332 @Test 333 public void testForwardingJavaFileManager(Path outerBase) throws Exception { 334 List<String> log = new ArrayList<>(); 335 JavaFileObject expectedOut = new SimpleJavaFileObject(new URI("Out.java"), JavaFileObject.Kind.SOURCE) {}; 336 337 FileObject fo1 = new SimpleJavaFileObject(new URI("Test1.java"), JavaFileObject.Kind.SOURCE) { 338 @Override 339 public String toString() { 340 return "Test1 - FO"; 341 } 342 }; 343 FileObject fo2 = new SimpleJavaFileObject(new URI("Test2.java"), JavaFileObject.Kind.SOURCE) { 344 @Override 345 public String toString() { 346 return "Test2 - FO"; 347 } 348 }; 349 350 JavaFileManager forwardingWithOverride = new ForwardingJavaFileManager<>(new MinimalJavaFileManager() { 351 @Override 352 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 353 log.add("getJavaFileForOutput(" + location + ", " + className + ", " + kind + ", " + sibling); 354 return expectedOut; 355 } 356 @Override 357 public JavaFileObject getJavaFileForOutputForOriginatingFiles(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject... originatingFiles) throws IOException { 358 throw new AssertionError("Should not be called."); 359 } 360 @Override 361 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 362 log.add("getFileForOutput(" + location + ", " + packageName + ", " + relativeName + ", " + sibling); 363 return expectedOut; 364 } 365 @Override 366 public FileObject getFileForOutputForOriginatingFiles(JavaFileManager.Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException { 367 throw new AssertionError("Should not be called."); 368 } 369 }) { 370 @Override 371 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 372 return super.getJavaFileForOutput(location, className, kind, sibling); 373 } 374 @Override 375 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 376 return super.getFileForOutput(location, packageName, relativeName, sibling); 377 } 378 }; 379 380 assertEquals(expectedOut, 381 forwardingWithOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2)); 382 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, Test1 - FO"), log); log.clear(); 383 384 assertEquals(expectedOut, 385 forwardingWithOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE)); 386 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, null"), log); log.clear(); 387 388 assertEquals(expectedOut, 389 forwardingWithOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2)); 390 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, Test1 - FO"), log); log.clear(); 391 assertEquals(expectedOut, 392 forwardingWithOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java")); 393 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, null"), log); log.clear(); 394 395 JavaFileManager forwardingWithOutOverride = new ForwardingJavaFileManager<>(new MinimalJavaFileManager() { 396 @Override 397 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 398 throw new AssertionError("Should not be called."); 399 } 400 @Override 401 public JavaFileObject getJavaFileForOutputForOriginatingFiles(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject... originatingFiles) throws IOException { 402 log.add("getJavaFileForOutputForOriginatingFiles(" + location + ", " + className + ", " + kind + ", " + List.of(originatingFiles)); 403 return expectedOut; 404 } 405 @Override 406 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 407 throw new AssertionError("Should not be called."); 408 } 409 @Override 410 public FileObject getFileForOutputForOriginatingFiles(JavaFileManager.Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException { 411 log.add("getFileForOutputForOriginatingFiles(" + location + ", " + packageName + ", " + relativeName + ", " + List.of(originatingFiles)); 412 return expectedOut; 413 } 414 }) {}; 415 416 assertEquals(expectedOut, 417 forwardingWithOutOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2)); 418 assertEquals(List.of("getJavaFileForOutputForOriginatingFiles(CLASS_OUTPUT, test.Test, SOURCE, [Test1 - FO, Test2 - FO]"), log); log.clear(); 419 420 assertEquals(expectedOut, 421 forwardingWithOutOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE)); 422 assertEquals(List.of("getJavaFileForOutputForOriginatingFiles(CLASS_OUTPUT, test.Test, SOURCE, []"), log); log.clear(); 423 424 assertEquals(expectedOut, 425 forwardingWithOutOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2)); 426 assertEquals(List.of("getFileForOutputForOriginatingFiles(CLASS_OUTPUT, test, Test.java, [Test1 - FO, Test2 - FO]"), log); log.clear(); 427 assertEquals(expectedOut, 428 forwardingWithOutOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java")); 429 assertEquals(List.of("getFileForOutputForOriginatingFiles(CLASS_OUTPUT, test, Test.java, []"), log); log.clear(); 430 } 431 432 class MinimalJavaFileManager implements JavaFileManager { 433 @Override 434 public ClassLoader getClassLoader(JavaFileManager.Location location) { 435 throw new UnsupportedOperationException("Not supported."); 436 } 437 @Override 438 public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { 439 throw new UnsupportedOperationException("Not supported."); 440 } 441 @Override 442 public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) { 443 throw new UnsupportedOperationException("Not supported."); 444 } 445 @Override 446 public boolean isSameFile(FileObject a, FileObject b) { 447 throw new UnsupportedOperationException("Not supported."); 448 } 449 @Override 450 public boolean handleOption(String current, Iterator<String> remaining) { 451 throw new UnsupportedOperationException("Not supported."); 452 } 453 @Override 454 public boolean hasLocation(JavaFileManager.Location location) { 455 throw new UnsupportedOperationException("Not supported."); 456 } 457 @Override 458 public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException { 459 throw new UnsupportedOperationException("Not supported."); 460 } 461 @Override 462 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 463 throw new UnsupportedOperationException("Not supported."); 464 } 465 @Override 466 public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relativeName) throws IOException { 467 throw new UnsupportedOperationException("Not supported."); 468 } 469 @Override 470 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 471 throw new UnsupportedOperationException("Not supported."); 472 } 473 @Override 474 public void flush() throws IOException { 475 throw new UnsupportedOperationException("Not supported."); 476 } 477 @Override 478 public void close() throws IOException { 479 throw new UnsupportedOperationException("Not supported."); 480 } 481 @Override 482 public int isSupportedOption(String option) { 483 throw new UnsupportedOperationException("Not supported."); 484 } 485 }; 486 487 private void assertEquals(Object expected, Object actual) throws AssertionError { 488 if (!expected.equals(actual)) { 489 throw new AssertionError("Unexpected output: " + actual + ", expected: " + expected); 490 } 491 } 492 493 } --- EOF ---