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 javax.annotation.processing.Filer; 58 import javax.annotation.processing.SupportedOptions; 59 import javax.lang.model.element.Element; 60 import javax.lang.model.element.ModuleElement; 61 import javax.lang.model.element.PackageElement; 62 import javax.lang.model.util.Elements; 63 import javax.tools.FileObject; 64 import javax.tools.ForwardingJavaFileManager; 65 import javax.tools.JavaFileManager; 66 import javax.tools.SimpleJavaFileObject; 67 import javax.tools.StandardLocation; 68 import toolbox.JavacTask; 69 import toolbox.TestRunner; 70 import toolbox.TestRunner.Test; 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 try (StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null)) { 138 List<String> testOutput = new ArrayList<>(); 139 JavaFileManager fm = new ForwardingJavaFileManager<JavaFileManager>(sjfm) { 140 @Override 141 public JavaFileObject getJavaFileForOutputForOriginatingFiles(Location location, 142 String className, 143 JavaFileObject.Kind kind, 144 FileObject... originatingFiles) throws IOException { 145 List.of(originatingFiles) 146 .stream() 147 .map(fo -> getInfo(fo)) 148 .forEach(testOutput::add); 149 return super.getJavaFileForOutputForOriginatingFiles(location, className, kind, originatingFiles); 150 } 151 @Override 152 public FileObject getFileForOutputForOriginatingFiles(Location location, 153 String packageName, 154 String relativeName, 155 FileObject... originatingFiles) throws IOException { 156 List.of(originatingFiles) 157 .stream() 158 .map(fo -> getInfo(fo)) 159 .forEach(testOutput::add); 160 return super.getFileForOutputForOriginatingFiles(location, packageName, relativeName, originatingFiles); 161 } 162 private String getInfo(FileObject fo) { 163 try { 164 JavaFileObject jfo = (JavaFileObject) fo; //the test only expects JavaFileObjects here: 165 JavaFileManager.Location location = jfo.getKind() == JavaFileObject.Kind.SOURCE 166 ? StandardLocation.SOURCE_PATH 167 : sjfm.getLocationForModule(StandardLocation.SYSTEM_MODULES, "java.base"); 168 String binaryName = inferBinaryName(location, jfo); 169 return binaryName + "(" + jfo.getKind() + ")"; 170 } catch (IOException ex) { 171 throw new AssertionError(ex); 172 } 173 } 174 }; 175 try { 176 String generatedData; 177 try (MemoryFileManager mfm = new MemoryFileManager(sjfm)) { 178 compiler.getTask(null, mfm, null, null, null, 179 List.of(new ToolBox.JavaSource("package test; public class Generated2 {}"))) 180 .call(); 181 generatedData = 182 Base64.getEncoder().encodeToString(mfm.getFileBytes(StandardLocation.CLASS_OUTPUT, "test.Generated2")); 183 } 184 List<String> options = List.of("-sourcepath", src.toString(), 185 "-processor", "TestOriginatingElements$P", 186 "-processorpath", System.getProperty("test.class.path"), 187 "--module-path", libClasses.toString(), 188 "--add-modules", "lib", 189 "-d", classes.toString(), 190 "-AgeneratedData=" + generatedData); 191 ToolProvider.getSystemJavaCompiler() 192 .getTask(null, fm, null, options, null, sjfm.getJavaFileObjects(tb.findJavaFiles(src))) 193 .call(); 194 List<String> expectedOriginatingFiles = List.of("t.T1(SOURCE)", 195 "java.lang.String(CLASS)", 196 "p.package-info(SOURCE)", 197 "lib1.package-info(CLASS)", 198 "module-info(SOURCE)", 199 "module-info(CLASS)", 200 "t.T2(SOURCE)", 201 "java.lang.CharSequence(CLASS)", 202 "p.package-info(SOURCE)", 203 "lib1.package-info(CLASS)", 204 "module-info(SOURCE)", 205 "module-info(CLASS)", 206 "t.T3(SOURCE)", 207 "java.lang.Exception(CLASS)", 208 "p.package-info(SOURCE)", 209 "lib1.package-info(CLASS)", 210 "module-info(SOURCE)", 211 "module-info(CLASS)"); 212 assertEquals(expectedOriginatingFiles, testOutput); 213 } catch (IOException ex) { 214 throw new IllegalStateException(ex); 215 } 216 } 217 } 218 219 @SupportedAnnotationTypes("*") 220 @SupportedOptions("generatedData") 221 public static class P extends AbstractProcessor { 222 int round; 223 @Override 224 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 225 if (round++ == 0) { 226 Elements elems = processingEnv.getElementUtils(); 227 ModuleElement mdl = elems.getModuleElement("m"); 228 ModuleElement java_base = elems.getModuleElement("java.base"); 229 PackageElement pack = elems.getPackageElement("p"); 230 PackageElement lib1Pack = elems.getPackageElement("lib1"); 231 PackageElement lib2Pack = elems.getPackageElement("lib2"); 232 Filer filer = processingEnv.getFiler(); 233 try { 234 filer.createSourceFile("test.Generated1", 235 element("t.T1"), 236 element("java.lang.String"), 237 pack, 238 lib1Pack, 239 lib2Pack, 240 mdl, 241 java_base).openOutputStream().close(); 242 try (OutputStream out = filer.createClassFile("test.Generated2", 243 element("t.T2"), 244 element("java.lang.CharSequence"), 245 pack, 246 lib1Pack, 247 lib2Pack, 248 mdl, 249 java_base).openOutputStream()) { 250 out.write(Base64.getDecoder().decode(processingEnv.getOptions().get("generatedData"))); 251 } 252 filer.createResource(StandardLocation.CLASS_OUTPUT, 253 "test", 254 "Generated3.txt", 255 element("t.T3"), 256 element("java.lang.Exception"), 257 pack, 258 lib1Pack, 259 lib2Pack, 260 mdl, 261 java_base).openOutputStream().close(); 262 } catch (IOException ex) { 263 throw new AssertionError(ex); 264 } 265 } 266 return false; 267 } 268 269 private Element element(String type) { 270 return processingEnv.getElementUtils().getTypeElement(type); 271 } 272 273 @Override 274 public SourceVersion getSupportedSourceVersion() { 275 return SourceVersion.latest(); 276 } 277 } 278 279 @Test 280 public void testVacuousJavaFileManager(Path outerBase) throws Exception { 281 List<String> log = new ArrayList<>(); 282 JavaFileObject expectedOut = new SimpleJavaFileObject(new URI("Out.java"), JavaFileObject.Kind.SOURCE) {}; 283 JavaFileManager fm = new MinimalJavaFileManager() { 284 @Override 285 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 286 log.add("getJavaFileForOutput(" + location + ", " + className + ", " + kind + ", " + sibling); 287 return expectedOut; 288 } 289 @Override 290 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 291 log.add("getFileForOutput(" + location + ", " + packageName + ", " + relativeName + ", " + sibling); 292 return expectedOut; 293 } 294 }; 295 296 FileObject fo1 = new SimpleJavaFileObject(new URI("Test1.java"), JavaFileObject.Kind.SOURCE) { 297 @Override 298 public String toString() { 299 return "Test1 - FO"; 300 } 301 }; 302 FileObject fo2 = new SimpleJavaFileObject(new URI("Test2.java"), JavaFileObject.Kind.SOURCE) { 303 @Override 304 public String toString() { 305 return "Test2 - FO"; 306 } 307 }; 308 309 assertEquals(expectedOut, 310 fm.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2)); 311 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, Test1 - FO"), log); log.clear(); 312 313 assertEquals(expectedOut, 314 fm.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE)); 315 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, null"), log); log.clear(); 316 317 assertEquals(expectedOut, 318 fm.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2)); 319 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, Test1 - FO"), log); log.clear(); 320 assertEquals(expectedOut, 321 fm.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java")); 322 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, null"), log); log.clear(); 323 } 324 325 @Test 326 public void testForwardingJavaFileManager(Path outerBase) throws Exception { 327 List<String> log = new ArrayList<>(); 328 JavaFileObject expectedOut = new SimpleJavaFileObject(new URI("Out.java"), JavaFileObject.Kind.SOURCE) {}; 329 330 FileObject fo1 = new SimpleJavaFileObject(new URI("Test1.java"), JavaFileObject.Kind.SOURCE) { 331 @Override 332 public String toString() { 333 return "Test1 - FO"; 334 } 335 }; 336 FileObject fo2 = new SimpleJavaFileObject(new URI("Test2.java"), JavaFileObject.Kind.SOURCE) { 337 @Override 338 public String toString() { 339 return "Test2 - FO"; 340 } 341 }; 342 343 JavaFileManager forwardingWithOverride = new ForwardingJavaFileManager<>(new MinimalJavaFileManager() { 344 @Override 345 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 346 log.add("getJavaFileForOutput(" + location + ", " + className + ", " + kind + ", " + sibling); 347 return expectedOut; 348 } 349 @Override 350 public JavaFileObject getJavaFileForOutputForOriginatingFiles(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject... originatingFiles) throws IOException { 351 throw new AssertionError("Should not be called."); 352 } 353 @Override 354 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 355 log.add("getFileForOutput(" + location + ", " + packageName + ", " + relativeName + ", " + sibling); 356 return expectedOut; 357 } 358 @Override 359 public FileObject getFileForOutputForOriginatingFiles(JavaFileManager.Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException { 360 throw new AssertionError("Should not be called."); 361 } 362 }) { 363 @Override 364 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 365 return super.getJavaFileForOutput(location, className, kind, sibling); 366 } 367 @Override 368 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 369 return super.getFileForOutput(location, packageName, relativeName, sibling); 370 } 371 }; 372 373 assertEquals(expectedOut, 374 forwardingWithOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2)); 375 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, Test1 - FO"), log); log.clear(); 376 377 assertEquals(expectedOut, 378 forwardingWithOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE)); 379 assertEquals(List.of("getJavaFileForOutput(CLASS_OUTPUT, test.Test, SOURCE, null"), log); log.clear(); 380 381 assertEquals(expectedOut, 382 forwardingWithOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2)); 383 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, Test1 - FO"), log); log.clear(); 384 assertEquals(expectedOut, 385 forwardingWithOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java")); 386 assertEquals(List.of("getFileForOutput(CLASS_OUTPUT, test, Test.java, null"), log); log.clear(); 387 388 JavaFileManager forwardingWithOutOverride = new ForwardingJavaFileManager<>(new MinimalJavaFileManager() { 389 @Override 390 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 391 throw new AssertionError("Should not be called."); 392 } 393 @Override 394 public JavaFileObject getJavaFileForOutputForOriginatingFiles(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject... originatingFiles) throws IOException { 395 log.add("getJavaFileForOutputForOriginatingFiles(" + location + ", " + className + ", " + kind + ", " + List.of(originatingFiles)); 396 return expectedOut; 397 } 398 @Override 399 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 400 throw new AssertionError("Should not be called."); 401 } 402 @Override 403 public FileObject getFileForOutputForOriginatingFiles(JavaFileManager.Location location, String packageName, String relativeName, FileObject... originatingFiles) throws IOException { 404 log.add("getFileForOutputForOriginatingFiles(" + location + ", " + packageName + ", " + relativeName + ", " + List.of(originatingFiles)); 405 return expectedOut; 406 } 407 }) {}; 408 409 assertEquals(expectedOut, 410 forwardingWithOutOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE, fo1, fo2)); 411 assertEquals(List.of("getJavaFileForOutputForOriginatingFiles(CLASS_OUTPUT, test.Test, SOURCE, [Test1 - FO, Test2 - FO]"), log); log.clear(); 412 413 assertEquals(expectedOut, 414 forwardingWithOutOverride.getJavaFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test.Test", JavaFileObject.Kind.SOURCE)); 415 assertEquals(List.of("getJavaFileForOutputForOriginatingFiles(CLASS_OUTPUT, test.Test, SOURCE, []"), log); log.clear(); 416 417 assertEquals(expectedOut, 418 forwardingWithOutOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java", fo1, fo2)); 419 assertEquals(List.of("getFileForOutputForOriginatingFiles(CLASS_OUTPUT, test, Test.java, [Test1 - FO, Test2 - FO]"), log); log.clear(); 420 assertEquals(expectedOut, 421 forwardingWithOutOverride.getFileForOutputForOriginatingFiles(StandardLocation.CLASS_OUTPUT, "test", "Test.java")); 422 assertEquals(List.of("getFileForOutputForOriginatingFiles(CLASS_OUTPUT, test, Test.java, []"), log); log.clear(); 423 } 424 425 class MinimalJavaFileManager implements JavaFileManager { 426 @Override 427 public ClassLoader getClassLoader(JavaFileManager.Location location) { 428 throw new UnsupportedOperationException("Not supported."); 429 } 430 @Override 431 public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { 432 throw new UnsupportedOperationException("Not supported."); 433 } 434 @Override 435 public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) { 436 throw new UnsupportedOperationException("Not supported."); 437 } 438 @Override 439 public boolean isSameFile(FileObject a, FileObject b) { 440 throw new UnsupportedOperationException("Not supported."); 441 } 442 @Override 443 public boolean handleOption(String current, Iterator<String> remaining) { 444 throw new UnsupportedOperationException("Not supported."); 445 } 446 @Override 447 public boolean hasLocation(JavaFileManager.Location location) { 448 throw new UnsupportedOperationException("Not supported."); 449 } 450 @Override 451 public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException { 452 throw new UnsupportedOperationException("Not supported."); 453 } 454 @Override 455 public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 456 throw new UnsupportedOperationException("Not supported."); 457 } 458 @Override 459 public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relativeName) throws IOException { 460 throw new UnsupportedOperationException("Not supported."); 461 } 462 @Override 463 public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 464 throw new UnsupportedOperationException("Not supported."); 465 } 466 @Override 467 public void flush() throws IOException { 468 throw new UnsupportedOperationException("Not supported."); 469 } 470 @Override 471 public void close() throws IOException { 472 throw new UnsupportedOperationException("Not supported."); 473 } 474 @Override 475 public int isSupportedOption(String option) { 476 throw new UnsupportedOperationException("Not supported."); 477 } 478 }; 479 480 private void assertEquals(Object expected, Object actual) throws AssertionError { 481 if (!expected.equals(actual)) { 482 throw new AssertionError("Unexpected output: " + actual + ", expected: " + expected); 483 } 484 } 485 486 } --- EOF ---