POC: JDK ClassModel -> ASM ClassReader
Brian Goetz
brian.goetz at oracle.com
Fri Jul 1 15:01:59 UTC 2022
Thanks for test-driving this!
> This works really well, congratulations to such a well-working first
> attempt. Some details stuck out however:
Good to hear.
> 1. It does not seem like it is possible to model “CROP” frames. The
> frames that are read include zero additional local variables, but they
> do not indicate how many local variables are cropped. I would suggest
> adding subinterfaces for all frame types to allow for the same pattern
> matching style that works well with the rest of the API. This way, the
> declaredLocals and declaredStack methods would only be available if
> relevant and the crop frame type could add a croppedLocals() : int method.
In general, the API for stack maps hasn't gotten a lot of attention, as
there is not a lot of user code that accesses stack maps at all, and we
generate stack maps as part of code generation. So I'm not surprised
there are missing bits or rough edges (same for next issue.) Are you
generating stack maps yourself? Is the generator in the library not
meeting your needs?
> 3. Debugging with toString works well for the most, but not all
> classes, for example subclasses of CodeElement have representations.
> It’s probably an oversight but it would be neat to add this quickly to
> make exploring the code easier.
We should make a pass through all the implementations and make sure that
toString is both present and uniform. I am not surprised there are gaps
here. That would be a fine "starter bug" for someone to grab.
> 4. The JDK code knows an attribute named CharacterRange. I must
> confess that I never heard of it and I could neither find
> documentation. I assume it is a JDK20 concept? This made me however
> think about how such “unknown” attributes can be handled. I would like
> to find a way to treat all attributes that I do not know or care about
> as an UnknownAttribute. This way, I could simply forward them as a
> binary dump, as I currently do it for custom attributes, for example
> to forward it to ASM. However, today there is no way to convert an
> attribute payload back to an array.
It is not a JDK20 concept; it is what is used by the `jcov` coverage
tool (and I believe others such as jacoco), from the CodeTools project.
Javac will generate this attribute if you ask it nicely.
You should just ignore attributes you don't understand; that's how
attributes were designed to work (there will always be new ones.) But
your point about getting the raw contents is well taken. Essentially,
your request is to raise `UnknownAttribute::contents` up to Attribute,
perhaps renaming to `rawContents`. This seems an easy and reasonable
addition.
> 5. I think the “findAttribute” method will be invoked a lot.
> Currently, it iterates over all attributes on each call. Ideally, this
> would only be done once to create a map of attributes with O(1) lookup
> speed. Of course, I could do this myself, but I think this would be
> such a common need that I wanted to suggest it as implementation for
> the official API, especially since the API feels like a map lookup.
> This could be done as easily as by storing attributes in a map after
> they are read for the first time as the attribute keys already are
> compared by identity.
We considered this, and early performance tests suggested this was a
loser. However, that was a lot of API and workload churn ago, before
AttributeMapper, which is interned, so we could try with an
IdentityHashMap here and see whether it perturbs the benchmarks.
> 6. I found that TypeAnnotation.CatchTarget offers an index of the
> exception table entry, additionally to being visited inline. I found
> that model a bit awkward as there is no indication of the index in the
> ExceptionCatch instruction that comes with the same pass. Also, there
> is no guarantee on when the type annotations are visited in relation
> to the try catch block. Ideally, it would be guaranteed that the
> annotation is visited directly after the ExceptionCatch pseudo
> instruction, to allow for an easy single-pass processing.
Yeah, everything about the type annotations attributes is a
disaster... Transforming while keeping the validity of the TA
attributes is essentially impossible, because it includes indexes into
things like exception tables, code attributes, tvar lists, tvar bounds
lists, etc etc. We can decode a TA attribute, we can give you a way to
write one, but for a while we seriously considered just _dropping TA
attributes on the floor during adaptation_ because they're so inherently
unstable.
The visitation order guarantee you ask for is pretty expensive, and
something that very very few people would care about. So I surely
wouldn't want to do that by default.
We've largely punted on doing anything better with type annotations, at
least up until now, because they're such a disaster. Still, we can
probably do better here. Open to suggestions.
> Finally, I have not yet started working on a ClassWriter equivalent.
> Here, I found that the style of the Consumer<ClassBuilder> to be
> incompatible with the way ASM works. This is of course a decision of
> style, but I would consider this a major difficulty to migrate current
> code at some point. Would it be an idea to offer both styles of class
> creation? The internal one, additionally to exposing an interface with
> the methods of DirectClassBuilder?
We've struggled with this as well when adapting the back end of
`javac`. This is a tricky issue. In theory, allowing users to ask for
a ClassBuilder is not hard, as you've sketched out. But I am really
worried it will become an attractive nuisance, where users will
instinctively reach for that style because its familiar and seems
"easier", and then will lose out on some features that the
Consumer<ClassBuilder> approach offers, such as managing branch offset
sizes. Success would be if this were allowed, but only used 1% of the
time. Failure would be 3% :)
Q: in this case, is it enough if only ClassBuilder has this option, or
do you need it for MethodBuilder and CodeBuilder as well?
More information about the classfile-api-dev
mailing list