Add classfile-api module: java.lang.classfile (JEP 484) parse, generate, transform with timing instrumentation, ASM 9.11.1 comparison, version limits, JDK 24/25/26 API diff
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b4VQVEJWetTT1dYzyJ
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.ParseDemo (25.0.5+8-LTS)
|
||||
== header ==
|
||||
bytes : 1488
|
||||
this : demo/Target
|
||||
super : java/lang/Object
|
||||
version : 69.0 (ClassFile.JAVA_25_VERSION = 69)
|
||||
flags : public,super
|
||||
constant pool : 69 slots
|
||||
|
||||
== fields ==
|
||||
final,private name Ljava/lang/String;
|
||||
private calls I
|
||||
|
||||
== methods ==
|
||||
public <init>(Ljava/lang/String;)V
|
||||
public,static fib(I)I
|
||||
public,static sumTo(I)J
|
||||
public greet(Ljava/lang/String;)Ljava/lang/String;
|
||||
public pause(J)V
|
||||
public,static boom()V
|
||||
public,static explode([I)I
|
||||
public calls()I
|
||||
|
||||
== class elements, in file order (the model is a stream of elements) ==
|
||||
AccessFlags
|
||||
ClassFileVersion
|
||||
Superclass
|
||||
Interfaces
|
||||
field name
|
||||
field calls
|
||||
method <init>
|
||||
method fib
|
||||
method sumTo
|
||||
method greet
|
||||
method pause
|
||||
method boom
|
||||
method explode
|
||||
method calls
|
||||
SourceFile=Target.java
|
||||
BoundInnerClassesAttribute
|
||||
|
||||
== fib(I)I, instruction by instruction ==
|
||||
ILOAD_0
|
||||
ICONST_2
|
||||
IF_ICMPGE
|
||||
ILOAD_0
|
||||
GOTO
|
||||
ILOAD_0
|
||||
ICONST_1
|
||||
ISUB
|
||||
INVOKESTATIC demo/Target.fib(I)I
|
||||
ILOAD_0
|
||||
ICONST_2
|
||||
ISUB
|
||||
INVOKESTATIC demo/Target.fib(I)I
|
||||
IADD
|
||||
IRETURN
|
||||
|
||||
== invoke targets in the whole class ==
|
||||
2 x demo/Target.fib
|
||||
1 x indy:makeConcatWithConstants
|
||||
1 x java/lang/IllegalStateException.<init>
|
||||
1 x java/lang/Object.<init>
|
||||
1 x java/lang/Thread.sleep
|
||||
|
||||
== a JDK class from the runtime image: java.lang.String ==
|
||||
bytes 50116, major 69, methods 171, fields 11, interfaces 5
|
||||
PASS Target.class is class-file version 69 (compiled with --release 25)
|
||||
PASS Target has 8 methods (constructor + 7)
|
||||
PASS fib calls itself twice
|
||||
PASS greet() uses invokedynamic string concatenation
|
||||
exit=0
|
||||
@@ -0,0 +1,13 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.GenerateDemo target/gen/demo (25.0.5+8-LTS)
|
||||
generated 501 bytes, major version 69 (wrote target/gen/demo/Generated.class)
|
||||
ClassFile.verify(bytes) -> 0 problem(s)
|
||||
PASS verify() finds nothing wrong with the generated class
|
||||
defineClass -> demo.Generated hidden=false findable by name=true
|
||||
[defineClass] add(2,3)=5 hello()=Hello from generated bytecode sumTo(100)=5050 classify(-4)=negative classify(4)=non-negative
|
||||
PASS defineClass: generated methods return the right values
|
||||
defineHiddenClass -> name starts with 'demo.Generated/': true hidden=true distinct from the normal class=true
|
||||
[hidden] add(2,3)=5 hello()=Hello from generated bytecode sumTo(100)=5050 classify(-4)=negative classify(4)=non-negative
|
||||
PASS hidden: generated methods return the right values
|
||||
Class.forName(hidden name) -> ClassNotFoundException (hidden classes cannot be found by name)
|
||||
PASS hidden class is hidden, the other is not
|
||||
exit=0
|
||||
@@ -0,0 +1,106 @@
|
||||
$ javap -c -p -v demo.Generated (the class GenerateDemo wrote; 25.0.5+8-LTS)
|
||||
public class demo.Generated
|
||||
minor version: 0
|
||||
major version: 69
|
||||
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
|
||||
this_class: #2 // demo/Generated
|
||||
super_class: #6 // java/lang/Object
|
||||
interfaces: 0, fields: 0, methods: 5, attributes: 0
|
||||
Constant pool:
|
||||
#1 = Utf8 demo/Generated
|
||||
#2 = Class #1 // demo/Generated
|
||||
#3 = Utf8 <init>
|
||||
#4 = Utf8 ()V
|
||||
#5 = Utf8 java/lang/Object
|
||||
#6 = Class #5 // java/lang/Object
|
||||
#7 = NameAndType #3:#4 // "<init>":()V
|
||||
#8 = Methodref #6.#7 // java/lang/Object."<init>":()V
|
||||
#9 = Utf8 add
|
||||
#10 = Utf8 (II)I
|
||||
#11 = Utf8 hello
|
||||
#12 = Utf8 ()Ljava/lang/String;
|
||||
#13 = Utf8 Hello from generated bytecode
|
||||
#14 = String #13 // Hello from generated bytecode
|
||||
#15 = Utf8 sumTo
|
||||
#16 = Utf8 (I)J
|
||||
#17 = Utf8 classify
|
||||
#18 = Utf8 (I)Ljava/lang/String;
|
||||
#19 = Utf8 negative
|
||||
#20 = String #19 // negative
|
||||
#21 = Utf8 non-negative
|
||||
#22 = String #21 // non-negative
|
||||
#23 = Utf8 Code
|
||||
#24 = Utf8 StackMapTable
|
||||
#25 = Utf8 java/lang/String
|
||||
#26 = Class #25 // java/lang/String
|
||||
{
|
||||
public demo.Generated();
|
||||
descriptor: ()V
|
||||
flags: (0x0001) ACC_PUBLIC
|
||||
Code:
|
||||
stack=1, locals=1, args_size=1
|
||||
0: aload_0
|
||||
1: invokespecial #8 // Method java/lang/Object."<init>":()V
|
||||
4: return
|
||||
|
||||
public static int add(int, int);
|
||||
descriptor: (II)I
|
||||
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
|
||||
Code:
|
||||
stack=2, locals=2, args_size=2
|
||||
0: iload_0
|
||||
1: iload_1
|
||||
2: iadd
|
||||
3: ireturn
|
||||
|
||||
public static java.lang.String hello();
|
||||
descriptor: ()Ljava/lang/String;
|
||||
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
|
||||
Code:
|
||||
stack=1, locals=0, args_size=0
|
||||
0: ldc #14 // String Hello from generated bytecode
|
||||
2: areturn
|
||||
|
||||
public static long sumTo(int);
|
||||
descriptor: (I)J
|
||||
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
|
||||
Code:
|
||||
stack=4, locals=4, args_size=1
|
||||
0: lconst_0
|
||||
1: lstore_1
|
||||
2: iconst_1
|
||||
3: istore_3
|
||||
4: iload_3
|
||||
5: iload_0
|
||||
6: if_icmpgt 20
|
||||
9: lload_1
|
||||
10: iload_3
|
||||
11: i2l
|
||||
12: ladd
|
||||
13: lstore_1
|
||||
14: iinc 3, 1
|
||||
17: goto 4
|
||||
20: lload_1
|
||||
21: lreturn
|
||||
StackMapTable: number_of_entries = 2
|
||||
frame_type = 253 /* append */
|
||||
offset_delta = 4
|
||||
locals = [ long, int ]
|
||||
frame_type = 15 /* same */
|
||||
|
||||
public static java.lang.String classify(int);
|
||||
descriptor: (I)Ljava/lang/String;
|
||||
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
|
||||
Code:
|
||||
stack=1, locals=1, args_size=1
|
||||
0: iload_0
|
||||
1: ifge 9
|
||||
4: ldc #20 // String negative
|
||||
6: goto 11
|
||||
9: ldc #22 // String non-negative
|
||||
11: areturn
|
||||
StackMapTable: number_of_entries = 2
|
||||
frame_type = 9 /* same */
|
||||
frame_type = 65 /* same_locals_1_stack_item */
|
||||
stack = [ class java/lang/String ]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.BrokenGenerate (25.0.5+8-LTS)
|
||||
DROP_STACK_MAPS: verify() -> 1 problem(s)
|
||||
Expecting a stackmap frame at branch target 8 in demo/Broken::pick(int) @1
|
||||
DROP_STACK_MAPS: defineClass -> java.lang.VerifyError
|
||||
Expecting a stackmap frame at branch target 8
|
||||
Exception Details:
|
||||
Location:
|
||||
PASS a branching method built without stack maps fails verification
|
||||
PASS the same code with default options verifies
|
||||
ireturn in a String method: build() succeeded (126 bytes)
|
||||
verify: Bad return type in demo/Wrong::bad() @1 (java/lang/String is not assignable from integer_type)
|
||||
PASS verify() catches a wrongly typed return
|
||||
PASS stack underflow is caught while building
|
||||
underflow: build() threw java.lang.IllegalArgumentException
|
||||
Operand stack underflow at bytecode offset 0 of method u()
|
||||
swapped labels: build() succeeded (173 bytes), verify() -> 0 problem(s)
|
||||
swapped labels: defineClass -> java.lang.ClassFormatError: Illegal exception table range in class file demo/Swapped
|
||||
PASS the JVM rejects the swapped exception-table labels
|
||||
exit=0
|
||||
@@ -0,0 +1,31 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.TransformDemo target/instrumented (25.0.5+8-LTS)
|
||||
original 1488 bytes, naive 1799 bytes, with exception handler 2155 bytes
|
||||
-- naive: time normal returns only (1799 bytes; verify() problems: 0)
|
||||
PASS naive: time normal returns only: demo.Target here is a different Class object than the one on the class path
|
||||
boom() threw java.lang.IllegalStateException: boom
|
||||
explode() threw ArrayIndexOutOfBoundsException
|
||||
fib recorded 177 call(s)
|
||||
sumTo recorded 1 call(s)
|
||||
greet recorded 1 call(s)
|
||||
pause recorded 1 call(s)
|
||||
boom recorded 0 call(s)
|
||||
explode recorded 0 call(s)
|
||||
PASS naive version never records the two methods that throw
|
||||
PASS fib(10) makes 177 calls and 177 timings were recorded (recursion included)
|
||||
-- full: also time when an exception leaves the method (2155 bytes; verify() problems: 0)
|
||||
PASS full: also time when an exception leaves the method: demo.Target here is a different Class object than the one on the class path
|
||||
boom() threw java.lang.IllegalStateException: boom
|
||||
explode() threw ArrayIndexOutOfBoundsException
|
||||
fib recorded 177 call(s)
|
||||
sumTo recorded 1 call(s)
|
||||
greet recorded 1 call(s)
|
||||
pause recorded 1 call(s)
|
||||
boom recorded 1 call(s)
|
||||
explode recorded 1 call(s)
|
||||
PASS exception-safe version records boom() and explode() once each
|
||||
PASS fib still recorded 177 times
|
||||
PASS sumTo and greet recorded once each
|
||||
PASS pause(50) was recorded as at least 50 ms (>= 50,000,000 ns)
|
||||
PASS fib(20) gives the same answer before and after
|
||||
PASS sumTo(12345) gives the same answer before and after
|
||||
exit=0
|
||||
@@ -0,0 +1,121 @@
|
||||
$ javap -c -p demo.Target (original, as javac produced it; fib and boom only)
|
||||
public static int fib(int);
|
||||
Code:
|
||||
0: iload_0
|
||||
1: iconst_2
|
||||
2: if_icmpge 9
|
||||
5: iload_0
|
||||
6: goto 22
|
||||
9: iload_0
|
||||
10: iconst_1
|
||||
11: isub
|
||||
12: invokestatic #13 // Method fib:(I)I
|
||||
15: iload_0
|
||||
16: iconst_2
|
||||
17: isub
|
||||
18: invokestatic #13 // Method fib:(I)I
|
||||
21: iadd
|
||||
22: ireturn
|
||||
|
||||
public static void boom();
|
||||
Code:
|
||||
0: new #31 // class java/lang/IllegalStateException
|
||||
3: dup
|
||||
4: ldc #33 // String boom
|
||||
6: invokespecial #35 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
|
||||
9: athrow
|
||||
|
||||
$ javap -c -p demo.Target (naive transform: records on normal return only)
|
||||
public static int fib(int);
|
||||
Code:
|
||||
0: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
3: lstore_1
|
||||
4: iload_0
|
||||
5: iconst_2
|
||||
6: if_icmpge 13
|
||||
9: iload_0
|
||||
10: goto 26
|
||||
13: iload_0
|
||||
14: iconst_1
|
||||
15: isub
|
||||
16: invokestatic #13 // Method fib:(I)I
|
||||
19: iload_0
|
||||
20: iconst_2
|
||||
21: isub
|
||||
22: invokestatic #13 // Method fib:(I)I
|
||||
25: iadd
|
||||
26: ldc #76 // String Target.fib
|
||||
28: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
31: lload_1
|
||||
32: lsub
|
||||
33: invokestatic #82 // Method demo/Timings.record:(Ljava/lang/String;J)V
|
||||
36: ireturn
|
||||
|
||||
public static void boom();
|
||||
Code:
|
||||
0: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
3: lstore_0
|
||||
4: new #31 // class java/lang/IllegalStateException
|
||||
7: dup
|
||||
8: ldc #33 // String boom
|
||||
10: invokespecial #35 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
|
||||
13: athrow
|
||||
|
||||
$ javap -c -p demo.Target (full transform: plus a catch-all handler)
|
||||
public static int fib(int);
|
||||
Code:
|
||||
0: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
3: lstore_1
|
||||
4: iload_0
|
||||
5: iconst_2
|
||||
6: if_icmpge 13
|
||||
9: iload_0
|
||||
10: goto 26
|
||||
13: iload_0
|
||||
14: iconst_1
|
||||
15: isub
|
||||
16: invokestatic #13 // Method fib:(I)I
|
||||
19: iload_0
|
||||
20: iconst_2
|
||||
21: isub
|
||||
22: invokestatic #13 // Method fib:(I)I
|
||||
25: iadd
|
||||
26: ldc #76 // String Target.fib
|
||||
28: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
31: lload_1
|
||||
32: lsub
|
||||
33: invokestatic #82 // Method demo/Timings.record:(Ljava/lang/String;J)V
|
||||
36: ireturn
|
||||
37: astore_3
|
||||
38: ldc #76 // String Target.fib
|
||||
40: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
43: lload_1
|
||||
44: lsub
|
||||
45: invokestatic #82 // Method demo/Timings.record:(Ljava/lang/String;J)V
|
||||
48: aload_3
|
||||
49: athrow
|
||||
Exception table:
|
||||
from to target type
|
||||
4 37 37 any
|
||||
|
||||
public static void boom();
|
||||
Code:
|
||||
0: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
3: lstore_0
|
||||
4: new #31 // class java/lang/IllegalStateException
|
||||
7: dup
|
||||
8: ldc #33 // String boom
|
||||
10: invokespecial #35 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
|
||||
13: athrow
|
||||
14: astore_2
|
||||
15: ldc #90 // String Target.boom
|
||||
17: invokestatic #74 // Method java/lang/System.nanoTime:()J
|
||||
20: lload_0
|
||||
21: lsub
|
||||
22: invokestatic #82 // Method demo/Timings.record:(Ljava/lang/String;J)V
|
||||
25: aload_2
|
||||
26: athrow
|
||||
Exception table:
|
||||
from to target type
|
||||
4 14 14 any
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
$ javap -public java.lang.classfile.** on each JDK, then diff by class (24: OpenJDK Runtime Environment Temurin-24.0.2+12 (build 24.0.2+12), 26: OpenJDK Runtime Environment Temurin-26.0.2.1+1 (build 26.0.2.1+1))
|
||||
|
||||
=== JDK 24 -> JDK 25 ===
|
||||
+ java.lang.classfile.ClassFile: public static final int JAVA_25_VERSION;
|
||||
~ java.lang.classfile.attribute.UnknownAttribute
|
||||
was: public interface java.lang.classfile.attribute.UnknownAttribute extends java.lang.classfile.Attribute<java.lang.classfile.attribute.UnknownAttribute>, java.lang.classfile.ClassElement, java.lang.classfile.MethodElement, java.lang.classfile.FieldElement {
|
||||
now: public interface java.lang.classfile.attribute.UnknownAttribute extends java.lang.classfile.Attribute<java.lang.classfile.attribute.UnknownAttribute>, java.lang.classfile.ClassElement, java.lang.classfile.MethodElement, java.lang.classfile.FieldElement, java.lang.classfile.CodeElement {
|
||||
+ java.lang.classfile.constantpool.ClassEntry: public abstract boolean matches(java.lang.constant.ClassDesc);
|
||||
+ java.lang.classfile.constantpool.MethodTypeEntry: public abstract boolean matches(java.lang.constant.MethodTypeDesc);
|
||||
+ java.lang.classfile.constantpool.ModuleEntry: public abstract boolean matches(java.lang.constant.ModuleDesc);
|
||||
+ java.lang.classfile.constantpool.PackageEntry: public abstract boolean matches(java.lang.constant.PackageDesc);
|
||||
+ java.lang.classfile.constantpool.StringEntry: public abstract boolean equalsString(java.lang.String);
|
||||
+ java.lang.classfile.constantpool.Utf8Entry: public abstract boolean isFieldType(java.lang.constant.ClassDesc);
|
||||
+ java.lang.classfile.constantpool.Utf8Entry: public abstract boolean isMethodType(java.lang.constant.MethodTypeDesc);
|
||||
239 classes in old, 239 in new, 9 difference(s)
|
||||
|
||||
=== JDK 25 -> JDK 26 ===
|
||||
+ java.lang.classfile.ClassFile: public static final int JAVA_26_VERSION;
|
||||
239 classes in old, 239 in new, 1 difference(s)
|
||||
|
||||
=== is it a preview API on JDK 24? ===
|
||||
$ javap -v java.lang.classfile.ClassFile | grep -ci PreviewFeature
|
||||
24.0.2+12: 0
|
||||
25.0.5+8-LTS: 0
|
||||
26.0.2.1+1: 0
|
||||
|
||||
=== @since of ClassFile ===
|
||||
jdk-24.0.2+12: * @since 24
|
||||
jdk-25.0.5+8: * @since 24
|
||||
jdk-26.0.2.1+1: * @since 24
|
||||
@@ -0,0 +1,18 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.AsmParse (25.0.5+8-LTS)
|
||||
ASM sees major version 69
|
||||
public <init>(Ljava/lang/String;)V
|
||||
public,static fib(I)I
|
||||
public,static sumTo(I)J
|
||||
public greet(Ljava/lang/String;)Ljava/lang/String;
|
||||
public pause(J)V
|
||||
public,static boom()V
|
||||
public,static explode([I)I
|
||||
public calls()I
|
||||
2 x demo/Target.fib
|
||||
1 x indy:makeConcatWithConstants
|
||||
1 x java/lang/IllegalStateException.<init>
|
||||
1 x java/lang/Object.<init>
|
||||
1 x java/lang/Thread.sleep
|
||||
PASS ASM and java.lang.classfile list identical method lines
|
||||
PASS ASM and java.lang.classfile count identical invoke targets
|
||||
exit=0
|
||||
@@ -0,0 +1,7 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.AsmGenerate (25.0.5+8-LTS)
|
||||
ASM generated 501 bytes; java.lang.classfile generated 501 bytes
|
||||
PASS ASM output passes ClassFile.verify()
|
||||
[ASM-generated] add(2,3)=5 hello()=Hello from generated bytecode sumTo(100)=5050 classify(-4)=negative classify(4)=non-negative
|
||||
PASS ASM-generated: generated methods return the right values
|
||||
PASS both generators emit the same method list
|
||||
exit=0
|
||||
@@ -0,0 +1,17 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.AsmInstrument (25.0.5+8-LTS)
|
||||
original 1488 bytes, ASM-instrumented 2152 bytes
|
||||
PASS ASM output passes ClassFile.verify()
|
||||
-- ASM AdviceAdapter version (2152 bytes; verify() problems: 0)
|
||||
PASS ASM AdviceAdapter version: demo.Target here is a different Class object than the one on the class path
|
||||
boom() threw java.lang.IllegalStateException: boom
|
||||
explode() threw ArrayIndexOutOfBoundsException
|
||||
fib recorded 177 call(s)
|
||||
sumTo recorded 1 call(s)
|
||||
greet recorded 1 call(s)
|
||||
pause recorded 1 call(s)
|
||||
boom recorded 1 call(s)
|
||||
explode recorded 1 call(s)
|
||||
PASS ASM version also records both throwing methods once
|
||||
PASS ASM version: fib recorded 177 times
|
||||
PASS ASM version: pause(50) recorded as at least 50 ms
|
||||
exit=0
|
||||
@@ -0,0 +1,22 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.VersionLimits (25.0.5+8-LTS)
|
||||
running on Java 25; ClassFile.latestMajorVersion() = 69; ASM Opcodes.V25=69, V28=72
|
||||
major 69:
|
||||
java.lang.classfile : parsed, 8 methods
|
||||
ASM 9.11.1 : parsed, 8 methods
|
||||
major 70:
|
||||
java.lang.classfile : java.lang.IllegalArgumentException: Unsupported class file version: 70
|
||||
ASM 9.11.1 : parsed, 8 methods
|
||||
major 72:
|
||||
java.lang.classfile : java.lang.IllegalArgumentException: Unsupported class file version: 72
|
||||
ASM 9.11.1 : parsed, 8 methods
|
||||
major 73:
|
||||
java.lang.classfile : java.lang.IllegalArgumentException: Unsupported class file version: 73
|
||||
ASM 9.11.1 : java.lang.IllegalArgumentException: Unsupported class file version 73.0
|
||||
major 99:
|
||||
java.lang.classfile : java.lang.IllegalArgumentException: Unsupported class file version: 99
|
||||
ASM 9.11.1 : java.lang.IllegalArgumentException: Unsupported class file version 99.0
|
||||
PASS major 69 parses with java.lang.classfile
|
||||
PASS one version above the JDK's own is rejected by java.lang.classfile
|
||||
PASS ASM 9.11.1 still reads major 72 (Java 28)
|
||||
PASS ASM 9.11.1 rejects major 73
|
||||
exit=0
|
||||
@@ -0,0 +1,3 @@
|
||||
Lines of code in the method(s) that do the job (non-blank, non-comment), counted by scripts/run-all.sh:
|
||||
generate demo.Generated java.lang.classfile 32 ASM 63
|
||||
timing instrumentation java.lang.classfile 61 ASM 43
|
||||
@@ -0,0 +1,18 @@
|
||||
--- run 1 of 3 ---
|
||||
running on Java 25.0.5+8-LTS, ASM 9.11.1; INDICATIVE numbers, thread CPU time, shared 2-vCPU sandbox, no JMH
|
||||
2000 classes from java.base, 7001 KiB; 4 warm-up rounds, median of 9
|
||||
instructions seen: java.lang.classfile 478741, ASM 478741
|
||||
parse + visit every instruction : java.lang.classfile 119.4 ms ASM 166.1 ms (ASM / classfile = 1.39)
|
||||
parse + copy + write back : java.lang.classfile 38.6 ms ASM 78.9 ms (ASM / classfile = 2.05)
|
||||
--- run 2 of 3 ---
|
||||
running on Java 25.0.5+8-LTS, ASM 9.11.1; INDICATIVE numbers, thread CPU time, shared 2-vCPU sandbox, no JMH
|
||||
2000 classes from java.base, 7001 KiB; 4 warm-up rounds, median of 9
|
||||
instructions seen: java.lang.classfile 478741, ASM 478741
|
||||
parse + visit every instruction : java.lang.classfile 63.9 ms ASM 166.2 ms (ASM / classfile = 2.60)
|
||||
parse + copy + write back : java.lang.classfile 26.8 ms ASM 88.1 ms (ASM / classfile = 3.29)
|
||||
--- run 3 of 3 ---
|
||||
running on Java 25.0.5+8-LTS, ASM 9.11.1; INDICATIVE numbers, thread CPU time, shared 2-vCPU sandbox, no JMH
|
||||
2000 classes from java.base, 7001 KiB; 4 warm-up rounds, median of 9
|
||||
instructions seen: java.lang.classfile 478741, ASM 478741
|
||||
parse + visit every instruction : java.lang.classfile 48.8 ms ASM 44.2 ms (ASM / classfile = 0.90)
|
||||
parse + copy + write back : java.lang.classfile 18.8 ms ASM 101.6 ms (ASM / classfile = 5.39)
|
||||
@@ -0,0 +1,22 @@
|
||||
$ unzip -l spring-core-7.0.10.jar | grep 'META-INF/versions/24/.*\.class' | wc -l
|
||||
9
|
||||
|
||||
$ unzip -l spring-core-7.0.10.jar | grep 'versions/24.*ClassFile'
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileAnnotationDelegate$Source.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileAnnotationDelegate.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata$Builder.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileMetadataReader.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileMetadataReaderFactory.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileMethodMetadata$Source.class
|
||||
META-INF/versions/24/org/springframework/core/type/classreading/ClassFileMethodMetadata.class
|
||||
|
||||
$ unzip -l spring-core-7.0.10.jar | grep -c 'org/springframework/asm/.*\.class' (the repackaged ASM is still there)
|
||||
37
|
||||
|
||||
$ javap -v -cp <jar>!/META-INF/versions/24 ...ClassFileMetadataReader | grep java/lang/classfile (does it really call the JDK API?)
|
||||
java/lang/classfile/ClassFile
|
||||
java/lang/classfile/ClassModel
|
||||
|
||||
$ unzip -p spring-core-7.0.10.jar META-INF/MANIFEST.MF | grep -i multi-release
|
||||
Multi-Release: true
|
||||
@@ -0,0 +1,7 @@
|
||||
$ java -cp classes:asm-9.11.1.jar... demo.AsmGotcha (25.0.5+8-LTS)
|
||||
readerFlags = 0 -> java.lang.IllegalArgumentException
|
||||
LocalVariablesSorter only accepts expanded frames (see ClassReader.EXPAND_FRAMES)
|
||||
PASS the message names the missing flag
|
||||
readerFlags = EXPAND_FRAMES -> 2152 bytes, verify() problems: 0
|
||||
PASS with EXPAND_FRAMES the output verifies
|
||||
exit=0
|
||||
Reference in New Issue
Block a user