Skip to content

Annotation processors can write CLASS_OUTPUT resources beside source inputs #298

Description

@realityforge

J2CL’s javac frontend configures SOURCE_OUTPUT when annotation processing is enabled, but does not configure CLASS_OUTPUT.

This affects annotation processors that use the standard API to create a class-output resource with an originating element:

processingEnv
    .getFiler()
    .createResource(
        StandardLocation.CLASS_OUTPUT,
        "",
        "META-INF/example/config.json",
        originatingElement);

On JDK 18 and later, javac propagates the originating element’s source file to the JavaFileManager as an originating file. When CLASS_OUTPUT has not been configured, javac may use that source file as the sibling placement hint.

Observed with JDK 21.0.9:

  • With only SOURCE_OUTPUT configured, the resource is written beside the originating source input.
  • The directory portion of the requested resource name is lost because javac’s sibling fallback uses the basename.
  • If the source path traverses a symlink, the write can cross that symlink and modify the underlying source tree.
  • The file is not a declared J2CL action output, so cached action results can hide the side effect.
  • Configuring CLASS_OUTPUT causes the complete resource path to be written beneath that output directory.

A minimal javac-only reproduction uses a processor containing the call above:

javac -proc:only -s /tmp/generated-sources \
  -processorpath processor.jar \
  -processor ExampleProcessor \
  path/to/Sample.java

Without -d, javac creates Sample.json beside Sample.java.

Adding a class-output directory:

javac -proc:only \
  -s /tmp/generated-sources \
  -d /tmp/class-output \
  -processorpath processor.jar \
  -processor ExampleProcessor \
  path/to/Sample.java

creates the intended resource at:

/tmp/class-output/META-INF/example/config.json

The issue appears to have been introduced when annotation processing was enabled in JavacParser by commit a0a25d6. Current J2CL still configures SOURCE_OUTPUT without configuring CLASS_OUTPUT.

Expected behavior

All files created by annotation processors should remain under action-local output directories. Processing must not write beside source inputs.

Local workaround

The local patch points CLASS_OUTPUT at J2CL’s existing action-local generated-source directory:

diff --git a/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavacParser.java b/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavacParser.java
--- a/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavacParser.java
+++ b/transpiler/java/com/google/j2cl/transpiler/frontend/javac/JavacParser.java
@@ -218,6 +218,8 @@
     fileManager.setLocationFromPaths(StandardLocation.CLASS_PATH, classPath);
     if (sourceGenPath != null) {
       fileManager.setLocationFromPaths(
+          StandardLocation.CLASS_OUTPUT, ImmutableList.of(sourceGenPath));
+      fileManager.setLocationFromPaths(
           StandardLocation.SOURCE_OUTPUT, ImmutableList.of(sourceGenPath));
     }

This was validated with a cache-disabled J2CL action. The processor resource was written beneath _sourcegen/META-INF/..., and no file was created beside the source input.

A separate action-local class-output directory would also address the issue if keeping generated sources and class resources separate is preferred.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions