Stack map generation problem

David Lloyd david.lloyd at redhat.com
Thu Dec 12 19:08:56 UTC 2024


Probably related: a few other classes which are being transformed with the
new method call are failing due to stack size calculation problems, like
this:

java.lang.IllegalArgumentException: Stack size mismatch at bytecode offset
204 of method discoverSources()
  - max stack: 65535
    max locals: 65535
    attributes: []
    //stack map frame @0: {locals:
[io/smallrye/config/SmallRyeConfigBuilder], stack: []}
    0: {opcode: NEW, type: java/util/ArrayList}
    3: {opcode: DUP}
    4: {opcode: INVOKESPECIAL, owner: java/util/ArrayList, method name:
<init>, method type: ()V}
    7: {opcode: ASTORE_1, slot: 1}
    8: {opcode: LDC, constant value: 'ClassDesc[ConfigSource]'}
    10: {opcode: ALOAD_0, slot: 0}
    11: {opcode: GETFIELD, owner: io/smallrye/config/SmallRyeConfigBuilder,
field name: classLoader, field type: Ljava/lang/ClassLoader;}
    14: {opcode: INVOKESTATIC, owner:
io/github/dmlloyd/modules/ModuleServiceLoader, method name: load, method
type: (Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/util/ServiceLoader;}
    17: {opcode: ASTORE_2, slot: 2}
    18: {opcode: ALOAD_2, slot: 2}
    19: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader, method
name: iterator, method type: ()Ljava/util/Iterator;}
    22: {opcode: ASTORE_3, slot: 3}
    23: {opcode: ALOAD_3, slot: 3}
    24: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
hasNext, method type: ()Z}
    29: {opcode: IFEQ, target: 55}
    32: {opcode: ALOAD_3, slot: 3}
    33: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
next, method type: ()Ljava/lang/Object;}
    38: {opcode: CHECKCAST, type:
org/eclipse/microprofile/config/spi/ConfigSource}
    41: {opcode: ASTORE, slot: 4}
    43: {opcode: ALOAD_1, slot: 1}
    44: {opcode: ALOAD, slot: 4}
    46: {opcode: INVOKEINTERFACE, owner: java/util/List, method name: add,
method type: (Ljava/lang/Object;)Z}
    51: {opcode: POP}
    52: {opcode: GOTO, target: 23}
    55: {opcode: LDC, constant value: 'ClassDesc[ConfigSourceProvider]'}
    57: {opcode: ALOAD_0, slot: 0}
    58: {opcode: GETFIELD, owner: io/smallrye/config/SmallRyeConfigBuilder,
field name: classLoader, field type: Ljava/lang/ClassLoader;}
    61: {opcode: INVOKESTATIC, owner:
io/github/dmlloyd/modules/ModuleServiceLoader, method name: load, method
type: (Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/util/ServiceLoader;}
    64: {opcode: ASTORE_3, slot: 3}
    65: {opcode: ALOAD_3, slot: 3}
    66: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader, method
name: iterator, method type: ()Ljava/util/Iterator;}
    69: {opcode: ASTORE, slot: 4}
    71: {opcode: ALOAD, slot: 4}
    73: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
hasNext, method type: ()Z}
    78: {opcode: IFEQ, target: 148}
    81: {opcode: ALOAD, slot: 4}
    83: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
next, method type: ()Ljava/lang/Object;}
    88: {opcode: CHECKCAST, type:
org/eclipse/microprofile/config/spi/ConfigSourceProvider}
    91: {opcode: ASTORE, slot: 5}
    93: {opcode: ALOAD, slot: 5}
    95: {opcode: ALOAD_0, slot: 0}
    96: {opcode: GETFIELD, owner: io/smallrye/config/SmallRyeConfigBuilder,
field name: classLoader, field type: Ljava/lang/ClassLoader;}
    99: {opcode: INVOKEINTERFACE, owner:
org/eclipse/microprofile/config/spi/ConfigSourceProvider, method name:
getConfigSources, method type:
(Ljava/lang/ClassLoader;)Ljava/lang/Iterable;}
    104: {opcode: INVOKEINTERFACE, owner: java/lang/Iterable, method name:
iterator, method type: ()Ljava/util/Iterator;}
    109: {opcode: ASTORE, slot: 6}
    111: {opcode: ALOAD, slot: 6}
    113: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
hasNext, method type: ()Z}
    118: {opcode: IFEQ, target: 145}
    121: {opcode: ALOAD, slot: 6}
    123: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
next, method type: ()Ljava/lang/Object;}
    128: {opcode: CHECKCAST, type:
org/eclipse/microprofile/config/spi/ConfigSource}
    131: {opcode: ASTORE, slot: 7}
    133: {opcode: ALOAD_1, slot: 1}
    134: {opcode: ALOAD, slot: 7}
    136: {opcode: INVOKEINTERFACE, owner: java/util/List, method name: add,
method type: (Ljava/lang/Object;)Z}
    141: {opcode: POP}
    142: {opcode: GOTO, target: 111}
    145: {opcode: GOTO, target: 71}
    148: {opcode: LDC, constant value: 'ClassDesc[ConfigSourceFactory]'}
    150: {opcode: ALOAD_0, slot: 0}
    151: {opcode: GETFIELD, owner:
io/smallrye/config/SmallRyeConfigBuilder, field name: classLoader, field
type: Ljava/lang/ClassLoader;}
    154: {opcode: INVOKESTATIC, owner:
io/github/dmlloyd/modules/ModuleServiceLoader, method name: load, method
type: (Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/util/ServiceLoader;}
    157: {opcode: ASTORE, slot: 4}
    159: {opcode: ALOAD, slot: 4}
    161: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader, method
name: iterator, method type: ()Ljava/util/Iterator;}
    164: {opcode: ASTORE, slot: 5}
    166: {opcode: ALOAD, slot: 5}
    168: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
hasNext, method type: ()Z}
    173: {opcode: IFEQ, target: 207}
    176: {opcode: ALOAD, slot: 5}
    178: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name:
next, method type: ()Ljava/lang/Object;}
    183: {opcode: CHECKCAST, type: io/smallrye/config/ConfigSourceFactory}
    186: {opcode: ASTORE, slot: 6}
    188: {opcode: ALOAD_1, slot: 1}
    189: {opcode: NEW, type: io/smallrye/config/ConfigurableConfigSource}
    192: {opcode: DUP}
    193: {opcode: ALOAD, slot: 6}
    195: {opcode: INVOKESPECIAL, owner:
io/smallrye/config/ConfigurableConfigSource, method name: <init>, method
type: (Lio/smallrye/config/ConfigSourceFactory;)V}
    198: {opcode: INVOKEINTERFACE, owner: java/util/List, method name: add,
method type: (Ljava/lang/Object;)Z}
    203: {opcode: POP}
    204: {opcode: GOTO, target: 166}
    207: {opcode: ALOAD_1, slot: 1}
    208: {opcode: ARETURN}

On Thu, Dec 12, 2024 at 11:11 AM David Lloyd <david.lloyd at redhat.com> wrote:

> Initial disclaimer: I'm using my backport [1] of this API, so it's
> possible that I've introduced a bug that is not found upstream, or somehow
> missed a backport patch. I don't have a good way to test this with upstream
> directly at the moment.
>
> I'm seeing a case where I'm transforming a class, and only changing calls
> to two overloads of `ServiceLoader.load()` to an equivalent on another
> class, for a test. But, the rewritten class triggers a `VerifyError`
> because one of the stack maps got a superfluous `int` added to it. This
> looks similar to a previously reported bug.
>
> I've narrowed it down to when I change the owner type of the method being
> called. If I keep the original owner, then things pass through just fine -
> this might be because of some optimization where it doesn't detect a
> change, or maybe it is because I end up growing the constant pool by one?
>
> Here's my transformation code (it's a little redundant in places but I was
> trying to rule out possible problems):
>             ClassFile cf = ClassFile.of();
>             ClassModel old = cf.parse(bytes);
>
>             // then later...
>
>             newBytes = cf.transformClass(old, old.thisClass(),
> ClassTransform.transformingMethodBodies(
>                 (cb, ce) -> {
>                     if (ce instanceof InvokeInstruction ii
>                         && ii.opcode() == Opcode.INVOKESTATIC
>                         &&
> ii.owner().name().equalsString("java/util/ServiceLoader")
>                         && ii.name().equalsString("load")
>                     ) {
>                         switch (ii.type().stringValue()) {
>                             case
> "(Ljava/lang/Class;)Ljava/util/ServiceLoader;",
>
>  "(Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/util/ServiceLoader;" ->
>
> cb.invokestatic(ModuleServiceLoader.class.describeConstable().orElseThrow(),
> ii.name().stringValue(), ii.typeSymbol(), ii.isInterface());
>                             default ->
>                                 cb.invokestatic(ii.owner().asSymbol(),
> ii.name().stringValue(), ii.typeSymbol(), ii.isInterface());
>                         }
>                     } else {
>                         cb.with(ce);
>                     }
>                 }
>             ));
>
> Here's the original with a correct stack map:
>
>   Stackmap Frame:
>     bci: @61
>     flags: { }
>     locals: { 'java/util/ServiceLoader', 'java/util/Iterator' }
>     stack: { integer }
>   Bytecode:
>     0000000: 1259 b800 5ab3 003b 125b b802 314b 2ab6
>     0000010: 0040 4c2b b900 4101 0099 0021 2bb9 0042
>     0000020: 0100 c000 5b4d 2cb9 005c 0100 125d b800
>     0000030: 30c0 005e b800 09a7 0006 b800 5fb8 0060
>     0000040: b800 61b2 0047 b300 2db2 0050 b300 4c11
>     0000050: 0080 b300 0510 20b3 0006 1101 00b3 0007
>     0000060: b1
>               - {start: 28, line number: 45}
>               - {start: 38, line number: 46}
>               - {start: 55, line number: 47}
>               - {start: 58, line number: 48}
>               - {start: 61, line number: 51}
>               - {start: 64, line number: 53}
>               - {start: 67, line number: 63}
>               - {start: 73, line number: 65}
>               - {start: 79, line number: 67}
>               - {start: 85, line number: 69}
>               - {start: 90, line number: 70}
>             local variables:
>               - {start: 38, end: 55, slot: 2, name: next, type:
> Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;}
>               - {start: 14, end: 67, slot: 0, name: executorLoader, type:
> Ljava/util/ServiceLoader;}
>               - {start: 19, end: 67, slot: 1, name: iterator, type:
> Ljava/util/Iterator;}
>             local variable types:
>               - {start: 14, end: 67, slot: 0, name: executorLoader,
> signature:
> Ljava/util/ServiceLoader<Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;>;}
>               - {start: 19, end: 67, slot: 1, name: iterator, signature:
> Ljava/util/Iterator<Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;>;}
>             stack map frames:
>                 58: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: []}
>                 61: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: []}
>             //stack map frame @0: {locals: [], stack: []}
>             0: {opcode: LDC, constant value:
> mutiny.disableCallBackDecorators}
>             2: {opcode: INVOKESTATIC, owner: java/lang/Boolean, method
> name: getBoolean, method type: (Ljava/lang/String;)Z}
>             5: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> DISABLE_CALLBACK_DECORATORS, field type: Z}
>             8: {opcode: LDC, constant value:
> 'ClassDesc[ExecutorConfiguration]'}
>             10: {opcode: INVOKESTATIC, owner: java/util/ServiceLoader,
> method name: load, method type:
> (Ljava/lang/Class;)Ljava/util/ServiceLoader;}
>             13: {opcode: ASTORE_0, slot: 0}
>             14: {opcode: ALOAD_0, slot: 0, type:
> Ljava/util/ServiceLoader;, variable name: executorLoader}
>             15: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader,
> method name: iterator, method type: ()Ljava/util/Iterator;}
>             18: {opcode: ASTORE_1, slot: 1}
>             19: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;,
> variable name: iterator}
>             20: {opcode: INVOKEINTERFACE, owner: java/util/Iterator,
> method name: hasNext, method type: ()Z}
>             25: {opcode: IFEQ, target: 58}
>             28: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;,
> variable name: iterator}
>             29: {opcode: INVOKEINTERFACE, owner: java/util/Iterator,
> method name: next, method type: ()Ljava/lang/Object;}
>             34: {opcode: CHECKCAST, type:
> io/smallrye/mutiny/infrastructure/ExecutorConfiguration}
>             37: {opcode: ASTORE_2, slot: 2}
>             38: {opcode: ALOAD_2, slot: 2, type:
> Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;, variable name:
> next}
>             39: {opcode: INVOKEINTERFACE, owner:
> io/smallrye/mutiny/infrastructure/ExecutorConfiguration, method name:
> getDefaultWorkerExecutor, method type: ()Ljava/util/concurrent/Executor;}
>             44: {opcode: LDC, constant value: executor}
>             46: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/helpers/ParameterValidation, method name: nonNull,
> method type: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;}
>             49: {opcode: CHECKCAST, type: java/util/concurrent/Executor}
>             52: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name:
> setDefaultExecutor, method type: (Ljava/util/concurrent/Executor;)V}
>             55: {opcode: GOTO, target: 61}
>             //stack map frame @58: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: []}
>             58: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name:
> setDefaultExecutor, method type: ()V}
>             //stack map frame @61: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: []}
>             61: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name: reload,
> method type: ()V}
>             64: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name:
> resetCanCallerThreadBeBlockedSupplier, method type: ()V}
>             67: {opcode: GETSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer,
> field name: INSTANCE, field type:
> Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer;}
>             70: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> droppedExceptionHandler, field type: Ljava/util/function/Consumer;}
>             73: {opcode: GETSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger,
> field name: INSTANCE, field type:
> Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger;}
>             76: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> operatorLogger, field type:
> Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger;}
>             79: {opcode: SIPUSH, constant value: 128}
>             82: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> multiOverflowDefaultBufferSize, field type: I}
>             85: {opcode: BIPUSH, constant value: 32}
>             87: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeXs,
> field type: I}
>             90: {opcode: SIPUSH, constant value: 256}
>             93: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeS,
> field type: I}
>             96: {opcode: RETURN}
>
> Here's the rewritten version with the wrong stack map:
>
>   Current Frame:
>     bci: @55
>     flags: { }
>     locals: { 'java/util/ServiceLoader', 'java/util/Iterator',
> 'io/smallrye/mutiny/infrastructure/ExecutorConfiguration' }
>     stack: { }
>   Stackmap Frame:
>     bci: @61
>     flags: { }
>     locals: { 'java/util/ServiceLoader', 'java/util/Iterator' }
>     stack: { integer }
>   Bytecode:
>     0000000: 1259 b800 5ab3 003b 125b b802 314b 2ab6
>     0000010: 0040 4c2b b900 4101 0099 0021 2bb9 0042
>     0000020: 0100 c000 5b4d 2cb9 005c 0100 125d b800
>     0000030: 30c0 005e b800 09a7 0006 b800 5fb8 0060
>     0000040: b800 61b2 0047 b300 2db2 0050 b300 4c11
>     0000050: 0080 b300 0510 20b3 0006 1101 00b3 0007
>     0000060: b1
>               - {start: 14, line number: 43}
>               - {start: 19, line number: 44}
>               - {start: 28, line number: 45}
>               - {start: 38, line number: 46}
>               - {start: 55, line number: 47}
>               - {start: 58, line number: 48}
>               - {start: 61, line number: 51}
>               - {start: 64, line number: 53}
>               - {start: 67, line number: 63}
>               - {start: 73, line number: 65}
>               - {start: 79, line number: 67}
>               - {start: 85, line number: 69}
>               - {start: 90, line number: 70}
>             stack map frames:
>                 58: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: []}
>                 61: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: [int]}
>             //stack map frame @0: {locals: [], stack: []}
>             0: {opcode: LDC, constant value:
> mutiny.disableCallBackDecorators}
>             2: {opcode: INVOKESTATIC, owner: java/lang/Boolean, method
> name: getBoolean, method type: (Ljava/lang/String;)Z}
>             5: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> DISABLE_CALLBACK_DECORATORS, field type: Z}
>             8: {opcode: LDC, constant value:
> 'ClassDesc[ExecutorConfiguration]'}
>             10: {opcode: INVOKESTATIC, owner:
> io/github/dmlloyd/modules/ModuleServiceLoader, method name: load, method
> type: (Ljava/lang/Class;)Ljava/util/ServiceLoader;}
>             13: {opcode: ASTORE_0, slot: 0}
>             14: {opcode: ALOAD_0, slot: 0, type:
> Ljava/util/ServiceLoader;, variable name: executorLoader}
>             15: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader,
> method name: iterator, method type: ()Ljava/util/Iterator;}
>             18: {opcode: ASTORE_1, slot: 1}
>             19: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;,
> variable name: iterator}
>             20: {opcode: INVOKEINTERFACE, owner: java/util/Iterator,
> method name: hasNext, method type: ()Z}
>             25: {opcode: IFEQ, target: 58}
>             28: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;,
> variable name: iterator}
>             29: {opcode: INVOKEINTERFACE, owner: java/util/Iterator,
> method name: next, method type: ()Ljava/lang/Object;}
>             34: {opcode: CHECKCAST, type:
> io/smallrye/mutiny/infrastructure/ExecutorConfiguration}
>             37: {opcode: ASTORE_2, slot: 2}
>             38: {opcode: ALOAD_2, slot: 2, type:
> Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;, variable name:
> next}
>             39: {opcode: INVOKEINTERFACE, owner:
> io/smallrye/mutiny/infrastructure/ExecutorConfiguration, method name:
> getDefaultWorkerExecutor, method type: ()Ljava/util/concurrent/Executor;}
>             44: {opcode: LDC, constant value: executor}
>             46: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/helpers/ParameterValidation, method name: nonNull,
> method type: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;}
>             49: {opcode: CHECKCAST, type: java/util/concurrent/Executor}
>             52: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name:
> setDefaultExecutor, method type: (Ljava/util/concurrent/Executor;)V}
>             55: {opcode: GOTO, target: 61}
>             //stack map frame @58: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: []}
>             58: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name:
> setDefaultExecutor, method type: ()V}
>             //stack map frame @61: {locals: [java/util/ServiceLoader,
> java/util/Iterator], stack: [int]}
>             61: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name: reload,
> method type: ()V}
>             64: {opcode: INVOKESTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, method name:
> resetCanCallerThreadBeBlockedSupplier, method type: ()V}
>             67: {opcode: GETSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer,
> field name: INSTANCE, field type:
> Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndD
> umpThrowableConsumer;}
>             70: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> droppedExceptionHandler, field type: Ljava/util/function/Consumer;}
>             73: {opcode: GETSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger,
> field name: INSTANCE, field type:
> Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger;}
>             76: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> operatorLogger, field type:
> Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger;}
>             79: {opcode: SIPUSH, constant value: 128}
>             82: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name:
> multiOverflowDefaultBufferSize, field type: I}
>             85: {opcode: BIPUSH, constant value: 32}
>             87: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeXs,
> field type: I}
>             90: {opcode: SIPUSH, constant value: 256}
>             93: {opcode: PUTSTATIC, owner:
> io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeS,
> field type: I}
>             96: {opcode: RETURN}
>
> [1] https://github.com/dmlloyd/jdk-classfile-backport
>
> --
> - DML • he/him
>


-- 
- DML • he/him
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20241212/16464028/attachment-0001.htm>


More information about the classfile-api-dev mailing list