[code-reflection] RFR: Add support for local classes [v2]
Maurizio Cimadamore
mcimadamore at openjdk.org
Tue Jun 4 17:51:20 UTC 2024
On Tue, 4 Jun 2024 17:47:32 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:
>> This PR adds support for local and anonymous instance class creation in the code model.
>>
>> This is a bit trickier than inner classes in that local classes can also capture local variables from enclosing scopes. And, to make things even trickier, some local classes can be used in a *pre-construction* environment, that is in a location where `this` is not available yet (e.g. because we're in the middle of a `super` constructor call).
>>
>> In this PR, we only add support for local and anonymous class that do **not** appear in a pre-construction context. The logic to compute the set of captured local is shared with `Lower` (as part of Java 8 that logic has been somewhat generalized to accommodate different clients, as lambda generation also needs to know the set of variables captured by a local class). The resulting captures are then added as arguments of the `new` operation, and the constructor signature is tweaked accordingly.
>>
>> For instance, given this:
>>
>>
>> String testLocalCaptureParam(String s) {
>> class Foo {
>> String m() { return s; }
>> }
>> return new Foo().m();
>> }
>>
>>
>> The following model is now generated:
>>
>>
>> func @"testLocalCaptureParam" (%0 : LocalClassTest, %1 : java.lang.String)java.lang.String -> {
>> %2 : Var<java.lang.String> = var %1 @"s";
>> %3 : java.lang.String = var.load %2;
>> %4 : .<LocalClassTest, LocalClassTest$2Foo> = new %0 %3 @"func<.<LocalClassTest, LocalClassTest$2Foo>, LocalClassTest, java.lang.String>";
>> %5 : java.lang.String = invoke %4 @".<LocalClassTest, LocalClassTest$2Foo>::m()java.lang.String";
>> return %5;
>> };
>>
>>
>> I've added a bunch of tests to check the shape of the resulting code model, plus a dynamic test to make sure that we can indeed build local classes by interpreting said model.
>
> Maurizio Cimadamore has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains one commit:
>
> Initial push
test/langtools/tools/javac/reflect/TestIRFromAnnotation.java line 63:
> 61: public class TestIRFromAnnotation {
> 62:
> 63: static final Set<String> EXCLUDED_TEST = Set.of(
I've added a list of tests to skip. First, trying to scan a testng test with this annotation processor doesn't work because certain testng dependencies cannot be resolved (but this is just noise in the test output).
More importantly, doing annotation processing on a method that contains local classes yields inconsistent local class names. Note that a local class name depend on _all_ the local classes contained within that compilation unit (e.g. if there's 3 methods, each declaring a class `Foo`, you get `Outer$1Foo`, `Outer$2Foo` and `Outer$3Foo`.
But the annotation processing code doesn't know that: whenever we retrieve the model for a method being compiled, we type-check the method on the fly, which means we generate "transient" inner class names, which ignore any other method. I think this is an intrinsic limitation on how annotation processors do things (in fact it would be much cleaner if we could extract the code model from a method being compiler _after_ type-checking, e.g. using a compiler plugin rather than an annotation processor. That's how null checker frameworks prefer to do things).
This will need to be addressed separately.
-------------
PR Review Comment: https://git.openjdk.org/babylon/pull/111#discussion_r1626403308
More information about the babylon-dev
mailing list