[code-reflection] RFR: Move code reflection API into incubating module

Maurizio Cimadamore mcimadamore at openjdk.org
Mon Nov 11 14:53:17 UTC 2024


This PR moves the code reflection API into a separate incubating module. In order to do that, Babylon compiler support (`ReflectMethods`) has morphed into a compiler plugin, which is optionally invoked when the user specifies the command line option `--add-modules jdk.incubator.module`.

#### Basic structure

The new module `jdk.incubator.module` has the following structure (starred packages are not exported):


src/jdk.incubator.code/share/classes
└── jdk
    └── incubator
        └── code
            ├── analysis
            ├── bytecode
            ├── compiler (*)
            ├── internal (*)
            ├── interpreter
            ├── op
            ├── parser
            │   └── impl (*)
            ├── tools
            │   ├── dot
            │   └── renderer
            ├── type
            │   └── impl (*)
            └── writer


In other words, the new module subsumes the sources under `java.base/java.lang.reflect.code` and those under `jdk.code.tools`.

#### Compiler plugin

To decouple the `ReflectMethods` class from `jdk,compiler` we have moved `ReflectMethods` in a new compiler plugin in the new module (see `compiler` package). Compiler plugins are loaded using regular `ServiceLoader` infrastructure. However, here we have an issue: incubator modules are not resolved by default (unless `--add-modules` is used). This means that javac will not look inside `jdk.incubator.code` when finding its plugins.

To workaround this issue, javac will create a new module layer which points at `jdk.incubator.module` and then call `ServiceLoader::load` by passing that layer (see `BasicJavacTask`). This will force the javac plugin to be loaded, and no additional runtime warnings to be displayed when running javac.

Some additional tweaks were needed to avoid the creation of a module layer if the incubator module is not present (e.g. if we're using javac in "boostrap mode") or if we're running javac from an exploded build (in which case the incubator module is part of the module graph).

Note, the `@CodeReflection` annotation has been moved to `jdk.incubator.code`.

#### Symbol constants

All code reflection-related symbol constants have migrated from `Symtab` to the `CodeReflectionSymbols` in the new module. This is necessary because the symbols are now defined in a module other than `java.base` and javac `Symtab` cannot resolve references to symbols outside `java.base` early on (`Symtab` is one of the first classes to be initialized in the compiler).

After some consideration, I've decided to move the creation of these symbols in a separate class, to avoid pesky bootstrapping issues.

#### `Quotable` lambda metafactory changes

Another obstacle is the implementation of the lambda metafactory changes to support the `Quotable` functional interface target. Since now `Quotable` is defined in its own module, the lambda metafactory can no longer resolve class literals in that new module (as `java.base` does not -- and cannot -- depend on the new module).

A similar trick to the one described above was implemented: a new module layer is created, pointing at `jdk.incubator.code` and runtime types for `Quotable` and other relevant classes are obtained from there.

#### Obtaining code models

There are currently two ways to obtain code models:

* At runtime, by calling `Method::getCodeModel`
* At compile-time, by calling `Elements::getBody`

Since neither `java.base` nor `jdk.compiler` can have direct dependencies on the new module, I've decided to move these factories into static methods in the `Op` class:

* `Op::ofMethod(Method)`
* `Op::ofElement(ProcessingEnvironment, ExecutableElement)

(Note: this allowed us to use a sharper signature in `Op::ofElement` which was previously returning `Object`.

#### Build changes

No particular change was required here. However, note that we have eliminated the need for generating copies of the code reflection classes into the jdk.compiler module.

#### Test changes

Most tests required at least one of the following three kinds of updates:

* tweak `import` statements to point at the new module
* add a `@module jdk.incubator.code` to the test header, so that the module is added to the module graph when compiling/running the test
* tweak code that obtains code model, either reflectively, or via an annotation processor.

All tests (in both compiler and jdk) pass.

#### Other dependencies

@PaulSandoz has fixed Hat, and all the various samples to use the new packages/module (thanks!). The only changes required are import statement tweaks.

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

Commit messages:
 - Fix whitespace
 - Fix jshell test
 - Merge branch 'code-reflection' into code_reflection_plugin+merge
 - Merge pull request #1 from PaulSandoz/plugin_updates
 - Updates to module and API changes
 - Make compiler tests more robust when ran in exploded build
 - Improve support for exploded JDK build
 - Fix imports in hat
 - Add missing code reflection classes
 - All jdk tests pass
 - ... and 6 more: https://git.openjdk.org/babylon/compare/86dfc369...7e846d2d

Changes: https://git.openjdk.org/babylon/pull/273/files
  Webrev: https://webrevs.openjdk.org/?repo=babylon&pr=273&range=00
  Stats: 4102 lines in 407 files changed: 1510 ins; 951 del; 1641 mod
  Patch: https://git.openjdk.org/babylon/pull/273.diff
  Fetch: git fetch https://git.openjdk.org/babylon.git pull/273/head:pull/273

PR: https://git.openjdk.org/babylon/pull/273


More information about the babylon-dev mailing list