Offset error when writing type annotations on NEW instructions

Rafael Winterhalter rafael.wth at gmail.com
Mon Nov 11 09:43:51 UTC 2024


Hei Chen,
just to let you know: I found an easy way to implement this by simply
creating a buffer of one Instruction where I can add instructions either
before or after the buffer where the former is now applied for
type-annotations. This way I do not need to create a label for each BCI and
my britch now yields the correct results.
Best regards, Rafael

Am Mo., 11. Nov. 2024 um 10:17 Uhr schrieb Rafael Winterhalter <
rafael.wth at gmail.com>:

> Hei Chen,
>
> I think I understand the problem then. With ASM, type annotations on
> elements are processed just after the element was encountered. From the
> javadoc of ASM: "Visits an annotation on an instruction. This method must
> be called just after the annotated instruction." In contrast, the Class
> File API requires a label to be inserted "just before" the instruction is
> added.
>
> Ideally, CodeBuilder would allow me to call something like
> "newBoundLabelOnPreviousInstruction", but I understand if you do not want
> to add something like it. If the Class File API allowed to add labels both
> prior or after encountering an instruction, one could work with both
> approaches of inserting type annotations prior or after an instruction is
> added. This would make migrating from ASM more straight-forward, too, as I
> assume that this is the prevalent model today with ASM being so dominant.
>
> Otherwise, I will need to work around and delay any ASM invocation for my
> bridge, to consider if a subsequent call represents a type annotation and
> apply a reordering this way. This is not a big deal, but of course it would
> be much easier solved if I could bind a label with a BCI offset further up
> the stack. Possibly, I could even add a label before any annotatable
> instruction, just in case. Would this be overly expensive to do?
>
> Cheers, Rafael
>
> Am Mo., 11. Nov. 2024 um 00:12 Uhr schrieb Chen Liang <
> chen.l.liang at oracle.com>:
>
>> Hi Rafael,
>> I believe this is due to a misunderstanding with how ClassFile API labels
>> work.  A ClassFile API label models an insertion point represented by a
>> BCI; so in your example, the bound label should be located right before the
>> new instruction, so that the next immediately executed instruction would be
>> the new instruction.  This is like how the labels for control flows work.
>>
>> Unfortunately, I understand this is distinct from the ASM model - ASM
>> visitor model treats this as if this is a tiny tree structure and considers
>> a type annotation as a subsidiary of an instruction, and this brings
>> challenges to porting the implementations.  Here's some context: we
>> ClassFile API developers long believed that type annotations interact
>> poorly with class file transformations because of hard-to-track arbitrary
>> references such as interface indices, and thus we did not integrate it as a
>> part of the ClassFile API code streaming model, and we drops type
>> annotation attributes in transformations by the attribute stability
>> settings.
>>
>> Regards,
>> Chen Liang
>> ------------------------------
>> *From:* classfile-api-dev <classfile-api-dev-retn at openjdk.org> on behalf
>> of Rafael Winterhalter <rafael.wth at gmail.com>
>> *Sent:* Saturday, November 9, 2024 3:09 PM
>> *To:* classfile-api-dev <classfile-api-dev at openjdk.org>
>> *Subject:* Offset error when writing type annotations on NEW instructions
>>
>> With the most recent build of JDK 24, I discover one test failure in my
>> test suite when comparing with ASM (
>> https://github.com/raphw/asm-jdk-bridge): Any type annotation that is
>> added to a "new" instruction is added to the subsequent instruction
>> instead. This might irritate parsers as the label will point to an
>> instruction that cannot normally be annotated, as NEW is normally followed
>> by DUP.
>>
>> I created a reproducers that compares a javac compiled class and one that
>> is created with the class file API. The error is likely in the attribute
>> mapper's write method as I cannot reproduce it when testing a class file
>> reader using the class file API compared to ASM. The reproducer is as
>> follows and is visualized in the first line of each of the two
>> representations where the correct byte code offset is 0, not 3 as the
>> annotation is added to the first instruction in the method:
>>
>>
>> import java.io.InputStream;
>> import java.lang.annotation.ElementType;
>> import java.lang.annotation.Retention;
>> import java.lang.annotation.RetentionPolicy;
>> import java.lang.annotation.Target;
>> import java.lang.classfile.*;
>> import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
>> import java.lang.classfile.instruction.LineNumber;
>> import java.lang.classfile.instruction.LocalVariable;
>> import java.lang.constant.ClassDesc;
>> import java.lang.constant.MethodTypeDesc;
>> import java.util.List;
>>
>> public class TypeAnnotationSample {
>>
>>     void m() {
>>         new @A Object();
>>     }
>>
>>     public static void main(String[] args) throws Exception {
>>         byte[] compiled;
>>         try (InputStream inputStream = TypeAnnotationSample.class.getClassLoader().getResourceAsStream(
>>             TypeAnnotationSample.class.getName().replace('.', '/') + ".class"
>>         )) {
>>             compiled = inputStream.readAllBytes();
>>         }
>>         print(compiled);
>>         System.out.println("-----");
>>         byte[] created = ClassFile.of().build(ClassDesc.of("Synthetic"), classBuilder -> {
>>             classBuilder.withMethod("m", MethodTypeDesc.of(void.class.describeConstable().orElseThrow()), 0, methodBuilder -> {
>>                 methodBuilder.withCode(codeBuilder -> {
>>                     codeBuilder.new_(Object.class.describeConstable().orElseThrow());
>>                     Label label = codeBuilder.newBoundLabel();
>>                     codeBuilder.with(RuntimeVisibleTypeAnnotationsAttribute.of(TypeAnnotation.of(
>>                             TypeAnnotation.TargetInfo.ofNewExpr(label),
>>                             List.of(),
>>                             Annotation.of(A.class.describeConstable().orElseThrow())
>>                     )));
>>                     codeBuilder.dup()
>>                             .invokespecial(Object.class.describeConstable().orElseThrow(), "<init>", MethodTypeDesc.of(void.class.describeConstable().orElseThrow()))
>>                             .pop()
>>                             .return_();
>>                 });
>>             });
>>         });
>>         print(created);
>>     }
>>
>>     private static void print(byte[] classFile) {
>>         ClassModel classModel = ClassFile.of().parse(classFile);
>>         MethodModel methodModel = classModel.methods().stream()
>>                 .filter(element -> element.methodName().equalsString("m"))
>>                 .findFirst()
>>                 .orElseThrow();
>>         CodeModel codeModel = methodModel.code().orElseThrow();
>>         codeModel.elementStream().forEach(element -> {
>>             switch (element) {
>>                 case LocalVariable _ -> { }
>>                 case LineNumber _ -> { }
>>                 case RuntimeVisibleTypeAnnotationsAttribute a -> System.out.println(a.annotations().stream()
>>                         .map(x -> x.annotation()
>>                                 + "@" + x.targetPath()
>>                                 + "/" + ((TypeAnnotation.OffsetTarget) x.targetInfo()).target())
>>                         .toList());
>>                 default -> System.out.println(element);
>>             }
>>         });
>>     }
>>
>>     @Retention(RetentionPolicy.RUNTIME)
>>     @Target(ElementType.TYPE_USE)
>>     @interface A {
>>     }
>> }
>>
>>
>> I think that the attribute mapper must subtract the bytes of the
>> instruction as it is currently adding the bytes at the end of the
>> instruction, not at its beginning. ASM and javac set the annotation offset
>> to the beginning of the statement, so I understand that this should be the
>> right approach.
>>
>> Thanks, Rafael
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20241111/87cecf8c/attachment.htm>


More information about the classfile-api-dev mailing list