Experience report

Dan Heidinga heidinga at redhat.com
Thu Aug 4 20:19:21 UTC 2022


Congrats to all involved in building this api!  It's been easy to pick
up and get started with.

My use case has been transforming classes to remove the use of
invokedynamic and runtime class generation for lambda expressions [0].
And I found the examples in the package javadoc made it very easy to
jump into the API and get something going right away.

The connection between this api and the j.l.Constants package is also
very convenient as it provides an easy path all the way from runtime
types to symbolic info to classfile data.

The examples in the package-summary give a good starting point, though
I stumbled slightly when switching from purely reading to actually
transforming classfiles.  I hit the "Writing classes" examples first
and rat-holed a bit there before realizing that I only needed the
ClassModel::transform call.  It might be worth re-ordering the flow to
put transformation first as in my experience that's more common than
writing new classfiles.

One of the areas I was modifying was the NEST_HOST and NEST_MEMBERS
attributes. Adapting an attribute, like adding new members to a
NEST_MEMBERS attribute, feels unpolished as it requires removing the
existing attribute and then adding an entirely new one.  It would feel
more straightforward if there was a way to generate a new attribute
from the old with additions.  Some kind of ::withSymbols method maybe?

I ended up reading the existing members, creating a new attribute from
the existing & new members, then replacing the entire attribute with
the new one.  It was easy to do with a composed transform (very nice
by the way!), but my code feels ugly in this case - is there a better
way to do an update of an attribute like this?

There are several ::of methods on NestMembersAttribute that help but
it might be worth adding a ::withSymbols(NestMembersAttribute,
ClassDesc... additions) api.  I can create a PR if there's interest in
these kinds of additions.

One of the areas I was slightly concerned about was object allocation.
I haven't measured it but using the APIs - especially mixing
ClassEntry & j.l.constant.ClassDesc - feels like it generates a lot of
temporary objects to get a set of entries into a consistent format.
I needed to convert from one format to the other to be able to create
new attributes - ie: read the set of ClassEntries and then convert
them to ClassDesc so they fit with the ClassDesc I already had in
hand. The ::with* apis additions might help here as well?  Or maybe
there's a better pattern for what I'm trying to do.  Guidance
appreciated.

Composable ClassTransforms are a very nice addition in the API.  After
writing the code that dropped an attribute, and replaced it with an
updated attribute, I was able to lift that into a composed transform
that could be reused in several places.

I also appreciated how small updates to the classfile are so easy,
straightforward, and clear. For example, this code sets the NestHost
based on a passed in ClassModel and replaces an existing byte[]:
            ClassModel cm = /* passed in value */
            ClassModel genModel = Classfile.parse(bytes);
            NestHostAttribute genHost = determineNestHost(cm);
            bytes = genModel.transform(ClassTransform.endHandler(b ->
b.with(genHost)));

That's been my experience so far - I'm eager to see this API shipping
in the JDK.

--Dan

[0] https://mail.openjdk.org/pipermail/leyden-dev/2022-August/000055.html



More information about the classfile-api-dev mailing list