[code-reflection] RFR: Add sample annotation processor using code models

Jan Lahoda jlahoda at openjdk.org
Wed Sep 10 11:13:05 UTC 2025


On Mon, 8 Sep 2025 13:49:50 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:

> This change adds a new sample of an annotation processor that uses code models to:
> * reject unsupported methods (e.g. `System.gc`, `System.exit`)
> * reject unsupported language construct (e.g. `try`, `throw`)
> 
> The annotation processor is somewhat configurable, and can be used by developers as an initial sketch upon which to build more custom compile-time checks.
> 
> When using the annotation processor to compile this:
> 
> 
> class Foo {
>    @CodeReflection
>    void test1() {
>       System.exit(1);
>    }
> 
>     @CodeReflection
>     void test2() {
>         System.gc();
>     }
> 
>     @CodeReflection
>     void test3() {
> 
>        try {
>            test2();
>        } finally {
>            test3();
>        }
> 
>     }
> }
> 
> 
> The following error messages are generated:
> 
> 
> Foo.java:5: error: System.exit not supported in reflectable methods
>    void test1() {
>         ^
> Foo.java:10: error: System.gc not supported in reflectable methods
>     void test2() {
>          ^
> Foo.java:15: error: try/catch statement not supported in reflectable methods
>     void test3() {
>          ^
> 
> 
> When putting this together, I noted some issues, reported below:
> 
> * javac was failing to execute annotation processors that depended on `jdk.incubator.code` module
> * obtaining the model from a method that contains attribution errors crashed the annotation processor
> * some packages (e.g. the ones in java.base) are not exported to the dynamic module layer's `jdk.incubator.code`
> * it is not possible to report the message at a more accurate location because the `Messager` API does not allow for this
> 
> Many thanks to @lahodaj for the invaluable help when fighting with module layers :-)

src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java line 1171:

> 1169:             ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[paths.size()]));
> 1170:             ModuleLayer bootLayer = ModuleLayer.boot();
> 1171:             Configuration cf = bootLayer.configuration().resolveAndBind(ModuleFinder.of(), finder, Collections.emptySet());

I would expect something like this:

            ModuleLayer augmentedModuleLayer;
            ClassLoader parentCL;
            if (CodeReflectionSupport.CODE_LAYER != null) {
                augmentedModuleLayer = CodeReflectionSupport.CODE_LAYER;
                parentCL = CodeReflectionSupport.CODE_LAYER.findLoader("jdk.incubator.code");
            } else {
                augmentedModuleLayer = bootLayer;
                parentCL = ClassLoader.getSystemClassLoader();
            }
            Configuration cf = augmentedModuleLayer.configuration().resolveAndBind(ModuleFinder.of(), finder, Collections.emptySet());
            ModuleLayer layer = augmentedModuleLayer.defineModulesWithOneLoader(cf, parentCL);
            return ServiceLoader.load(layer, service);


here rather than the `CODE_LAYER != null` test below.

-------------

PR Review Comment: https://git.openjdk.org/babylon/pull/554#discussion_r2330387940


More information about the babylon-dev mailing list