[code-reflection] RFR: Make @Reflect a use-site annotation

Maurizio Cimadamore mcimadamore at openjdk.org
Thu Dec 4 13:43:52 UTC 2025


On Thu, 4 Dec 2025 13:27:57 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:

> In the current iteration of compiler support there are two ways to make a lambda reflectable:
> 1. cast the lambda to a type that is annotated with the `@Reflect` annotation (use-site)
> 2. pass the lambda to a functional interface whose declaration is annotated with `@Reflect` (decl-site)
> 
> While (2) is handy in cases where the library wants to control reflectability of the lambdas it receives, this model is too weak and insecure, as it might lead to unknowing clients exposing private impl details with frameworks using code reflection.
> 
> For this reason, we have revamped the `@Reflect` annotation to control, at the use-site, which program elements should support code reflection:
> 
> * If a method is annotated with `@Reflect`, the method itself is reflectable, and all the lambdas it contains are reflectable;
> * If a field/local variable is annotated with `@Reflect`, all the lambdas that occur inside the initializer (if present), are reflectable;
> * If a cast type is annotated with `@Reflect` and the cast is applied to a lambda expression, that lambda expression is reflectable (this is the same as (1) above).
> 
> This means it's no longer possible for a library to create a reflectable functional interface, like:
> 
> 
> @Reflect
> interface ReflectableRunnable extends Runnable { }
> 
> 
> And unilaterally decide, for _all_ clients whether their lambdas should be reflectable.
> 
> To be clear, this is **not** how things will work longer term, but it's an interim step which provides the desired level of safety w/o requiring the additional language features.
> 
> I've fixed all tests. I've tried to simplify both javac and JDK tests by dropping redundant functional interface declarations.
> 
> I also fixed all the samples; for ONNX some tweaks were done, and I've decided to drop the OnnxFunction -- and to use a supplier instead. But if that's not desirable I can change back (or it can be changed back later).
> 
> I also fixed HAT tests -- in that case it was a bit trickier, as there's lots of tests that end up passing reflectable lambdas to the HAT's compute method. Luckily, all these tests are annotated with `@HatTest`, so I've simply added an extra `@Reflect` to any such test method. Since `@Reflect` now propagates to the body, this guarantees that all the lambdas inside these test methods are treated as reflectable.
> 
> Of course these HAT change end up enabling reflection for a lot more than it's required. I'll leave it to people more expert with HAT to clean that up, if needed.
> In the meantime, it was ...

src/jdk.incubator.code/share/classes/jdk/incubator/code/Reflect.java line 32:

> 30: 
> 31: /**
> 32:  * A program element annotated with this annotation enables code reflection in the annotated program element,

I've tried to describe a bit more formally how the annotation is supposed to behave -- @PaulSandoz  please take a look.

src/jdk.incubator.code/share/classes/jdk/incubator/code/Reflect.java line 49:

> 47:  * </ul>
> 48:  */
> 49: @Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD,

One thing I realized: when we made this a type annotation, now you can also use `@Reflect` on classes and constructors as well -- because in those cases the annotation is reinterpreted as a type annotation (ugly, I know) -- and so it will be silently ignored. One alternative would be to drop the annotated cast and just rely on local variable decl annotations, but I didn't want to go there w/o first asking.

src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java line 330:

> 328:             if (currentClassSym.type.getEnclosingType().hasTag(CLASS)) {
> 329:                 // Reflectable method references in local classes are not supported
> 330:                 log.warning(tree, ReflectableMrefInnerClass(currentClassSym.enclClass()));

Note: this is no longer an error -- otherwise we'd get errors for any reflectable method that contains local classes with some methods.

src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java line 1036:

> 1034:                     }
> 1035:                 }
> 1036:                 case PACKAGE, INTERFACE, CLASS, RECORD, ENUM -> {

This is a pre-existing issue -- e.g. calling `println` with a fully qualified name, like `java.lang.System.out.println` causes an exception, because we eventually visit `java` and `java.lang` which are packages

src/jdk.incubator.code/share/classes/jdk/incubator/code/internal/ReflectMethods.java line 1499:

> 1497:             JavaType fiType = typeToTypeElement(tree.target);
> 1498:             // build functional lambda
> 1499:             Op lambdaOp = JavaOp.lambda(fiType, stack.body, true);

Note: now _all_ lambdas we encounter when scanning a reflectable body are (by definition) quotable. This means that in several tests, the `isQuotable` attribute now shows up as `true` everywhere. And, in the JDK bytecode tests there is no longer a way to test that support for non-reflectable lambdas works correctly (so I dropped some tests there).

test/jdk/java/lang/reflect/code/bytecode/lift/TestBytecodeLift.java line 372:

> 370:     }
> 371: 
> 372:     static int consumeQuotable(int i, IntUnaryOperator f) {

Note: all lambdas nested inside a `@Reflect` method are reflectable now, so it's no longer possible (I think) to test lifting of non-reflectable lambdas.

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

PR Review Comment: https://git.openjdk.org/babylon/pull/726#discussion_r2589109180
PR Review Comment: https://git.openjdk.org/babylon/pull/726#discussion_r2589116428
PR Review Comment: https://git.openjdk.org/babylon/pull/726#discussion_r2589120431
PR Review Comment: https://git.openjdk.org/babylon/pull/726#discussion_r2589097043
PR Review Comment: https://git.openjdk.org/babylon/pull/726#discussion_r2589126947
PR Review Comment: https://git.openjdk.org/babylon/pull/726#discussion_r2589130474


More information about the babylon-dev mailing list