<div dir="ltr"><div>With the most recent build of JDK 24, I discover one test failure in my test suite when comparing with ASM (<a href="https://github.com/raphw/asm-jdk-bridge">https://github.com/raphw/asm-jdk-bridge</a>): 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.</div><div><br></div><div>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:</div><div><br></div><div><div style="background-color:rgb(30,31,34);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace;font-size:9.8pt"><br><span style="color:rgb(207,142,109)">import </span>java.io.InputStream;<br><span style="color:rgb(207,142,109)">import </span>java.lang.annotation.ElementType;<br><span style="color:rgb(207,142,109)">import </span>java.lang.annotation.<span style="color:rgb(179,174,96)">Retention</span>;<br><span style="color:rgb(207,142,109)">import </span>java.lang.annotation.RetentionPolicy;<br><span style="color:rgb(207,142,109)">import </span>java.lang.annotation.<span style="color:rgb(179,174,96)">Target</span>;<br><span style="color:rgb(207,142,109)">import </span>java.lang.classfile.*;<br><span style="color:rgb(207,142,109)">import </span>java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;<br><span style="color:rgb(207,142,109)">import </span>java.lang.classfile.instruction.LineNumber;<br><span style="color:rgb(207,142,109)">import </span>java.lang.classfile.instruction.LocalVariable;<br><span style="color:rgb(207,142,109)">import </span>java.lang.constant.ClassDesc;<br><span style="color:rgb(207,142,109)">import </span>java.lang.constant.MethodTypeDesc;<br><span style="color:rgb(207,142,109)">import </span>java.util.List;<br><br><span style="color:rgb(207,142,109)">public class </span>TypeAnnotationSample {<br><br>    <span style="color:rgb(207,142,109)">void </span><span style="color:rgb(86,168,245)">m</span>() {<br>        <span style="color:rgb(207,142,109)">new </span><span style="color:rgb(179,174,96)">@A </span>Object();<br>    }<br><br>    <span style="color:rgb(207,142,109)">public static void </span><span style="color:rgb(86,168,245)">main</span>(String[] args) <span style="color:rgb(207,142,109)">throws </span>Exception {<br>        <span style="color:rgb(207,142,109)">byte</span>[] compiled;<br>        <span style="color:rgb(207,142,109)">try </span>(InputStream inputStream = TypeAnnotationSample.<span style="color:rgb(207,142,109)">class</span>.getClassLoader().getResourceAsStream(<br>            TypeAnnotationSample.<span style="color:rgb(207,142,109)">class</span>.getName().replace(<span style="color:rgb(106,171,115)">'.'</span>, <span style="color:rgb(106,171,115)">'/'</span>) + <span style="color:rgb(106,171,115)">".class"<br></span><span style="color:rgb(106,171,115)">        </span>)) {<br>            compiled = inputStream.readAllBytes();<br>        }<br>        <span style="font-style:italic">print</span>(compiled);<br>        System.<span style="color:rgb(199,125,187);font-style:italic">out</span>.println(<span style="color:rgb(106,171,115)">"-----"</span>);<br>        <span style="color:rgb(207,142,109)">byte</span>[] created = ClassFile.<span style="font-style:italic">of</span>().build(ClassDesc.<span style="font-style:italic">of</span>(<span style="color:rgb(106,171,115)">"Synthetic"</span>), classBuilder -> {<br>            classBuilder.withMethod(<span style="color:rgb(106,171,115)">"m"</span>, MethodTypeDesc.<span style="font-style:italic">of</span>(<span style="color:rgb(207,142,109)">void</span>.<span style="color:rgb(207,142,109)">class</span>.describeConstable().orElseThrow()), <span style="color:rgb(42,172,184)">0</span>, methodBuilder -> {<br>                methodBuilder.withCode(codeBuilder -> {<br>                    codeBuilder.new_(Object.<span style="color:rgb(207,142,109)">class</span>.describeConstable().orElseThrow());<br>                    Label label = codeBuilder.newBoundLabel();<br>                    codeBuilder.with(RuntimeVisibleTypeAnnotationsAttribute.<span style="font-style:italic">of</span>(TypeAnnotation.<span style="font-style:italic">of</span>(<br>                            TypeAnnotation.TargetInfo.<span style="font-style:italic">ofNewExpr</span>(label),<br>                            List.<span style="font-style:italic">of</span>(),<br>                            Annotation.<span style="font-style:italic">of</span>(<span style="color:rgb(179,174,96)">A</span>.<span style="color:rgb(207,142,109)">class</span>.describeConstable().orElseThrow())<br>                    )));<br>                    codeBuilder.dup()<br>                            .invokespecial(Object.<span style="color:rgb(207,142,109)">class</span>.describeConstable().orElseThrow(), <span style="color:rgb(106,171,115)">"<init>"</span>, MethodTypeDesc.<span style="font-style:italic">of</span>(<span style="color:rgb(207,142,109)">void</span>.<span style="color:rgb(207,142,109)">class</span>.describeConstable().orElseThrow()))<br>                            .pop()<br>                            .return_();<br>                });<br>            });<br>        });<br>        <span style="font-style:italic">print</span>(created);<br>    }<br><br>    <span style="color:rgb(207,142,109)">private static void </span><span style="color:rgb(86,168,245)">print</span>(<span style="color:rgb(207,142,109)">byte</span>[] classFile) {<br>        ClassModel classModel = ClassFile.<span style="font-style:italic">of</span>().parse(classFile);<br>        MethodModel methodModel = classModel.methods().stream()<br>                .filter(element -> element.methodName().equalsString(<span style="color:rgb(106,171,115)">"m"</span>))<br>                .findFirst()<br>                .orElseThrow();<br>        CodeModel codeModel = methodModel.code().orElseThrow();<br>        codeModel.elementStream().forEach(element -> {<br>            <span style="color:rgb(207,142,109)">switch </span>(element) {<br>                <span style="color:rgb(207,142,109)">case </span>LocalVariable _ -> { }<br>                <span style="color:rgb(207,142,109)">case </span>LineNumber _ -> { }<br>                <span style="color:rgb(207,142,109)">case </span>RuntimeVisibleTypeAnnotationsAttribute a -> System.<span style="color:rgb(199,125,187);font-style:italic">out</span>.println(a.annotations().stream()<br>                        .map(x -> x.annotation()<br>                                + <span style="color:rgb(106,171,115)">"@" </span>+ x.targetPath()<br>                                + <span style="color:rgb(106,171,115)">"/" </span>+ ((TypeAnnotation.OffsetTarget) x.targetInfo()).target())<br>                        .toList());<br>                <span style="color:rgb(207,142,109)">default </span>-> System.<span style="color:rgb(199,125,187);font-style:italic">out</span>.println(element);<br>            }<br>        });<br>    }<br><br>    <span style="color:rgb(179,174,96)">@Retention</span>(RetentionPolicy.<span style="color:rgb(199,125,187);font-style:italic">RUNTIME</span>)<br>    <span style="color:rgb(179,174,96)">@Target</span>(ElementType.<span style="color:rgb(199,125,187);font-style:italic">TYPE_USE</span>)<br>    @<span style="color:rgb(207,142,109)">interface </span><span style="color:rgb(179,174,96)">A </span>{<br>    }<br>}</pre></div></div><div><br></div><div>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.</div><div><br></div><div>Thanks, Rafael<br></div></div>