API suggestions
Brian Goetz
brian.goetz at oracle.com
Wed Mar 22 13:29:04 UTC 2023
Here's the combinator I had in mind:
static<T extends CodeElement> CodeTransform
statefulReplace(Class<T> target,
Function<T, T> replacer,
Supplier<T> supplier) {
return new CodeTransform() {
boolean found = false;
@Override
public void accept(CodeBuilder builder, CodeElement element) {
if (target.isAssignableFrom(element.getClass())) {
found = true;
builder.accept(replacer.apply((T) element));
}
}
@Override
public void atEnd(CodeBuilder builder) {
if (!found)
builder.accept(supplier.get());
}
};
}
So then you could say
CodeTransform = statefulReplace(RVAA.class,
rvaa -> RVAA.of(...),
() -> RVAA.of(...));
It's cute, but I don't think it carries its weight in the API. I think
the real gap here is one of documentation, not abstraction; finding the
low-energy-state solution is not obvious enough. Once you find the
solution, making it incrementally smaller with a combinator (or ten) is
not as important.
Public Service Announcement: people think that code is the important
contribution, but sometimes doc is a more valuable contribution. A
"transform writers guide" would be an awesome contribution.
On 3/22/2023 8:42 AM, Dan Heidinga wrote:
>
>
> On Wed, Mar 22, 2023 at 8:19 AM Brian Goetz <brian.goetz at oracle.com>
> wrote:
>
> Before we get into what API changes are needed, let's sync on the
> problem first.
>
> 1. CodeTransform has an `atStart` method just as it has an
> `atEnd` method, and I presume overriding `atStart(builder)` is
> adequate to your need. Is the issue simply that there is no
> canned combinator for startHandler as there is for endHandler?
>
> 2. Adding an annotation is complicated, and we explored a number
> of solutions before settling in the current place. There are
> three cases:
>
> - There is no RVAA, so you have to add one
> - There is an RVAA, but it already has your annotation, so
> there's nothing to do
> - There is an RVAA, and it doesn't have your annotation, so you
> have to transform it
>
> (Actually there is a fourth case: it has your annotation, but with
> _different properties_; there is @Foo(3) but you want to add
> @Foo(4). Now what do you do? Replace it? Leave it? Add both?)
>
> The trickiness is compounded by the fact that the latter two cases
> can be handled by transforming the element for RVAA, but the first
> case can't be handled until the end handler, and you have to
> remember whether you saw an RVAA or not. So the process is
> inherently stateful. Some options were covered in this thread
> (which talked about NestMembers, but the story is the same for
> annotations.)
>
> https://mail.openjdk.org/pipermail/classfile-api-dev/2022-August/000108.html
>
> Here's the recommended code for that situation:
>
> () -> new ClassTransform() {
> boolean foundNM = false;
>
> void accept(ClassBuilder b, ClassElement e) {
> switch (e) {
> case NestMembersAttribute a -> { transform a;
> foundNM =
> true; }
> ... other transform stuff ...
> }
> }
>
> void atEnd(ClassBuilder b) {
> if (!foundNM)
> builder.accept(NestMembersAttribute.of(...));
> }
> }
>
> It's irritating because you have to do three things -- keep track
> of some state, rewrite an attribute if you see it, and generate an
> attribute if you don't, but it's really not so lengthy or hard to
> follow.
>
>
> Here's a link to my various attempts to find a better way to express
> this [0] when exploring NestMember attribute changes. Wrapping
> Brian's suggested pattern in a named class makes it a little easier to
> read at the point of use but there's no single pattern for this (that
> I've found) that results in significantly cleaner code.
>
> TLDR: Brian's proposed code is the best you can do for this.
>
> --Dan
>
> [0]
> https://github.com/DanHeidinga/jdk-sandbox/blob/9c5bf15522e76ebc106a433758c87623dec4827b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateLambdaClassesPlugin.java#L238-L385
> <https://urldefense.com/v3/__https://github.com/DanHeidinga/jdk-sandbox/blob/9c5bf15522e76ebc106a433758c87623dec4827b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateLambdaClassesPlugin.java*L238-L385__;Iw!!ACWV5N9M2RV99hQ!J-O_EVx6bVisxcY4VatFAgX4IqHCqDFZv9lj0hkNJQT9QCbPXVxQCCzwbWh-hHP8ZbPTFRJrET9BugBPlJc$>
>
>
> I can imagine capturing this in a combinator, where you provide
> two lambdas, one a transform on your favorite attribute, and one a
> supplier of that attribute, but I don't think it will be that much
> easier to use, it just automate managing and acting on the boolean.
>
>
>
>
> On 3/21/2023 11:38 PM, - wrote:
>> Hi,
>> After housekeeping the few tests after the latest integration of
>> additional constants in ConstantDescs, I have found two potentially
>> common patterns in bytecode manipulation and wish to suggest new APIs:
>>
>> 1. Injection of code in the beginning of methods
>> Currently, our CodeTransform only has an endHandler; however, this is
>> less useful, for a method can have multiple exit points. Usually to
>> track method calls, bytecode manipulation, such as those in jfr and
>> instrumentation, inject at the beginning of methods, which is
>> guaranteed to be on the code path.
>>
>> Thus, I suggest a `startHandler(Consumer<CodeBuilder>)` for
>> CodeTransform for this purpose, instead of having to explicitly
>> override atStart(CodeBuilder) in non-lambda CodeTransform.
>>
>> 2. Adding new annotations
>> In ASM, annotation injection is done simply via extra annotation
>> visitor calls, which makes it compatible for either single or multiple
>> annotations. In contrast, in the Classfile API, if we want to add an
>> annotation, feeding a new RuntimeVisibleAnnotationAttribute will
>> destroy the previous RuntimeVisibleAnnotationAttribute, and handling
>> via a stateful ClassTransform is a bit too lengthy for users.
>>
>> I don't know if we should have an API like Map::computeIfAbsent for
>> attributes or if we will simplify it via other means.
>>
>> Chen Liang
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20230322/c7ee4af4/attachment-0001.htm>
More information about the classfile-api-dev
mailing list