labels
Mark Roberts
markro at cs.washington.edu
Wed Jan 29 00:39:51 UTC 2025
I am using the technique we discussed to do my instrumentation:
- Get a copy of the code.elementList()
- Insert new instructions into this list, and/or modify existing ones
- Output the modified instruction list using the codeBuilder.with()
method
The problem I am having is I don’t see how to create new labels since I am
not allowed to insert a LabelTarget into my modified code list. My vague
idea is that I have to keep track of their locations in a separate list and
then call codeBuilder.labelBinding(my new label) at the appropriate place
in between calls to codeBuilder.with()?
That seems weird – there must be a better way?
Thank you,
Mark
*From:* Chen Liang <chen.l.liang at oracle.com>
*Sent:* Wednesday, November 27, 2024 4:01 PM
*To:* Mark Roberts <markro at cs.washington.edu>; classfile-api-dev at openjdk.org
*Subject:* Re: [External] : more class file transformation questions
Hi Mark,
1. No, you have to do new ArrayList<>(code.elementList()) as the list
returned is immutable.
2. To insert an instruction, just use List::add. To get an instruction,
create one from the factory methods (usually named "of") of Instruction
subclasses. Once you are done with the list, do
for (var e : list) codeBuilder.with(e);
3. Bound and unbound instructions have no difference in usage when you
write them. The bound instruction can be seen as a specialized version of
unbound instruction, that they are cheaper for the API to obtain and
supports fast copying to another CodeBuilder when condition allows (that
is, the new CodeBuilder has a constantpool that extends from the constant
pool of the bound instruction). Bound instructions can only be read from
Class Files; unbound instructions can always be created by factory methods
on the instruction interfaces.
Regards, Chen
------------------------------
*From:* Mark Roberts <markro at cs.washington.edu>
*Sent:* Wednesday, November 27, 2024 5:54 PM
*To:* Chen Liang <chen.l.liang at oracle.com>; classfile-api-dev at openjdk.org <
classfile-api-dev at openjdk.org>
*Subject:* RE: [External] : more class file transformation questions
Okay, a List<CodeElement> sounds interesting.
1. Would I just use CodeModel::elementList() as my initial list or do I
have to make a copy somehow?
2. How would I insert a new instruction into this list? Builders don’t
seem to return an item, perhaps I need to use the static instruction of()
methods?
3. What is difference between Bound and Unbound instructions?
Thank you,
Mark
*From:* Chen Liang <chen.l.liang at oracle.com>
*Sent:* Tuesday, November 19, 2024 3:25 PM
*To:* Mark Roberts <markro at cs.washington.edu>; classfile-api-dev at openjdk.org
*Subject:* Re: [External] : more class file transformation questions
Hi Mark,
ClassFileBuilder::with takes an element object, such as a FieldModel for a
ClassBuilder. It does not modify the object when applied to the new
builder, except implementation details like constant pool references..
ClassFileBuilder::transform takes the same type of the object as the object
to build, such as a ClassModel for a ClassBuilder. In the same time, it
allows you to process all element objects of that same type of object.
For example, a ClassBuilder can with a method or a field. A ClassBuilder
can transform a class, and use this to merge classes, like
clb.transform(sourceClassA,
ClassTransform.ACCEPT_ALL).transform(sourceClassB,
ClassTransform.ACCEPT_ALL)
to merge source classes A and B into the ongoing class builder.
For your specific use case, I think what you need is a method transform
that does something like:
(mb, me) -> {
if (mb instanceof CodeModel code) {
List<CodeElement> codeElements = ...// prepare your list of
instructions
mb.withCode(codeElements::forEach); // the list of instructions get
sent to the code builderOK
} else {
mb.with(me);
}
}
It is also possible to do this in CodeTransform by collecting everything in
a list in accept(CodeBuilder, CodeElement) and only working on the builder
in atEnd(CodeBuilder), when you can see all the elements. But that code
will be a bit not straightforward compared to this pattern I am showing
off, and this CodeTransform can only see all elements that is from one
original model instead of those from all original models (applying the
merge class A and B example).
P.S. When you reply, remember to choose "reply all" so that your reply gets
sent to the classfile-api-dev list too.
Regards,
Chen Liang
------------------------------
*From:* Mark Roberts <markro at cs.washington.edu>
*Sent:* Tuesday, November 19, 2024 4:43 PM
*To:* Chen Liang <chen.l.liang at oracle.com>
*Subject:* [External] : more class file transformation questions
Thank you, those suggestions really helped!
Next question, given a builder, I’m curious about the differences between
the ‘with’ versus ‘transform’ methods. I see how the transforms can
simplify coding and they support composition. The implication seems to be
that with and build create new objects from old while transform modifies
existing objects. But based on the examples I’ve seen they look very
similar. Are there significant performance differences? Or other
advantages? My use case, adding instrumentation code to class files,
requires inspecting every byte code and possibly modifying a lot of them.
I worry that I would have to composite many transforms where one linear
walk through the byte codes would be sufficient. Am I missing something?
(it wouldn’t surprise me if I was)
Thank you,
Mark
*From:* Chen Liang <chen.l.liang at oracle.com>
*Sent:* Sunday, November 17, 2024 4:59 PM
*To:* Mark Roberts <markro at cs.washington.edu>; classfile-api-dev at openjdk.org
*Subject:* Re: [External] : RE: class file transformation questions
Hi Mark,
For performing a lot of modifications, you first can create a
private static void check(CodeBuilder builder, CodeElement element) {
// check the elements, and if it matches a pattern, call another method
// as long as you pass the CodeBuilder and make changes to it, your
changes will be saved
}
And then refer to this as a CodeTransform in a method reference.
Or, if you want to do a huge overhaul to a CodeModel, you can get a
MethodTransform, and have a specific method like:
if (me instanceof CodeModel code) {
methodBuilder.withCode(cob -> rebuildCode(cob, code));
}
private static void rebuildCode(CodeBuilder builder, CodeModel model) {}
This gives you a more holistic view of the code, and should be more
friendly for massive transformations that almost rebuild the method code.
(This one also allows you to pull instructions to a list with new
ArrayList<>(model.elementList()), modify that list, and do
list.forEach(builder) to send the results, which is used in some older
patterns)
Regards,
Chen Liang
------------------------------
*From:* Mark Roberts <markro at cs.washington.edu>
*Sent:* Sunday, November 17, 2024 6:30 PM
*To:* Chen Liang <chen.l.liang at oracle.com>
*Subject:* [External] : RE: class file transformation questions
Thank you for your help, it is much appreciated. I have what I guess you
would call a coding style question. If you wish to perform a lot of
modifications to a class file I could envision a method with hundreds of
lines of code. For a smaller example, see the chaining multiple
transformations example in java/lang/classfile/ClassFileTransform.html. I
find this very (extremely?) difficult to read. Is there any way of writing
this code in a more ‘traditional’ way with several, smaller method bodies?
Or is there no way to use java.lang.classfile without chaining together
large numbers of builders written as lambda functions?
Thank you,
Mark
*From:* Chen Liang <chen.l.liang at oracle.com>
*Sent:* Friday, November 15, 2024 4:48 PM
*To:* Mark Roberts <markro at cs.washington.edu>; classfile-api-dev at openjdk.org
*Subject:* Re: class file transformation questions
Hi Mark,
1. For injecting code, you can override the atStart of the CodeTransform
interface. Unfortunately we don't have a utility method for you to pass a
lambda 🙁 but we can always add it later. If you wish to find an
injection point, you can get the full list of code elements, and find your
insertion point and add the desired instruction objects.
2. For adding new methods, you can also override the atStart/atEnd
method of your ClassTransform, or do andThen(ClassTransform.endHandler(clb
-> clb.withMethod(...))) to add methods.
3. To duplicate an existing method object, you can first call withMethod
to create a new method, and once you have the method builder, it has a
transform(otherMethod, transform) that can pipe otherMethod's contents
through the transform, and finally everything goes to the method builder.
Feel free to ask more or suggest. Unfortunately I am still trying to
improve documentations for ClassFile API, so at this stage the documents
may still be insufficient, and many of the useful methods are not covered
by the JEP.
Regards,
Chen Liang
------------------------------
*From:* classfile-api-dev <classfile-api-dev-retn at openjdk.org> on behalf of
Mark Roberts <markro at cs.washington.edu>
*Sent:* Friday, November 15, 2024 1:42 PM
*To:* classfile-api-dev at openjdk.org <classfile-api-dev at openjdk.org>
*Subject:* class file transformation questions
Several of our tools use the java.lang.instrument package to instrument
class files when they are loaded. We currently use BCEL to do the
instrumentation, but would like to move to the new java.lang.classfile
package. I have gotten some basic transforms working in this environment
and I see how to modify existing instructions. However, we need to
perform some larger modifications to the classes we instrument.
There are three basic transforms we need to perform (all on the same class
file):
1. Injecting code into an existing method
2. Adding new methods
3. Duplicating an existing method with some changes
Any suggestions as to how to accomplish these tasks would be much
appreciated.
Thank you,
Mark Roberts
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20250128/f61f3e55/attachment-0001.htm>
More information about the classfile-api-dev
mailing list