<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Hi David,</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
I took a look at your example. Seems the only transformation done is intercepting the call at BCI 10. To confirm this is a problem with StackMapGenerator, can you create a ClassFile object with the Option StackMapsOption.GENERATE_STACK_MAPS, which forcibly
discards the original stack map table to generate a new stack map table?</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Also, you said that "I don't have a good way to test this with upstream directly at the moment." I think you can just get this Infrastructure class file, and process this file with JDK 24 and see.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Is this the problematic code that broke? <a href="https://github.com/smallrye/smallrye-mutiny/blob/fa30c10e3e6fb1ca372b923d4ebe20b07433633c/implementation/src/main/java/io/smallrye/mutiny/infrastructure/Infrastructure.java#L41-L54">
https://github.com/smallrye/smallrye-mutiny/blob/fa30c10e3e6fb1ca372b923d4ebe20b07433633c/implementation/src/main/java/io/smallrye/mutiny/infrastructure/Infrastructure.java#L41-L54</a></div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
I will grab the Infrastructure class in io.smallrye.reactive:mutiny:2.7.0 and check this resource class file in a recent JDK 24 build. Please inform me if this is not the right binary, such as if you reproduced with a fork, an earlier version, or an ad-hoc
patch.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Regards,</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Chen Liang</div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> classfile-api-dev <classfile-api-dev-retn@openjdk.org> on behalf of David Lloyd <david.lloyd@redhat.com><br>
<b>Sent:</b> Thursday, December 12, 2024 11:11 AM<br>
<b>To:</b> classfile-api-dev@openjdk.org <classfile-api-dev@openjdk.org><br>
<b>Subject:</b> Stack map generation problem</font>
<div> </div>
</div>
<div>
<div dir="ltr">
<div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">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.</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">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.</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">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?</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">Here's my transformation code (it's a little redundant in places but I was trying to rule out possible problems):</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"> ClassFile cf = ClassFile.of();<br>
ClassModel old = cf.parse(bytes);<br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"> // then later...</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"> newBytes = cf.transformClass(old, old.thisClass(), ClassTransform.transformingMethodBodies(<br>
(cb, ce) -> {<br>
if (ce instanceof InvokeInstruction ii<br>
&& ii.opcode() == Opcode.INVOKESTATIC<br>
&& ii.owner().name().equalsString("java/util/ServiceLoader")<br>
&& <a href="http://ii.name">ii.name</a>().equalsString("load")<br>
) {<br>
switch (ii.type().stringValue()) {<br>
case "(Ljava/lang/Class;)Ljava/util/ServiceLoader;",<br>
"(Ljava/lang/Class;Ljava/lang/ClassLoader;)Ljava/util/ServiceLoader;" -><br>
cb.invokestatic(ModuleServiceLoader.class.describeConstable().orElseThrow(),
<a href="http://ii.name">ii.name</a>().stringValue(), ii.typeSymbol(), ii.isInterface());<br>
default -><br>
cb.invokestatic(ii.owner().asSymbol(), <a href="http://ii.name">
ii.name</a>().stringValue(), ii.typeSymbol(), ii.isInterface());<br>
}<br>
} else {<br>
cb.with(ce);<br>
}<br>
}<br>
));<br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">Here's the original with a correct stack map:</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"> Stackmap Frame:<br>
bci: @61<br>
flags: { }<br>
locals: { 'java/util/ServiceLoader', 'java/util/Iterator' }<br>
stack: { integer }<br>
Bytecode:<br>
0000000: 1259 b800 5ab3 003b 125b b802 314b 2ab6<br>
0000010: 0040 4c2b b900 4101 0099 0021 2bb9 0042<br>
0000020: 0100 c000 5b4d 2cb9 005c 0100 125d b800<br>
0000030: 30c0 005e b800 09a7 0006 b800 5fb8 0060<br>
0000040: b800 61b2 0047 b300 2db2 0050 b300 4c11<br>
0000050: 0080 b300 0510 20b3 0006 1101 00b3 0007<br>
0000060: b1 <br>
- {start: 28, line number: 45}<br>
- {start: 38, line number: 46}<br>
- {start: 55, line number: 47}<br>
- {start: 58, line number: 48}<br>
- {start: 61, line number: 51}<br>
- {start: 64, line number: 53}<br>
- {start: 67, line number: 63}<br>
- {start: 73, line number: 65}<br>
- {start: 79, line number: 67}<br>
- {start: 85, line number: 69}<br>
- {start: 90, line number: 70}<br>
local variables: <br>
- {start: 38, end: 55, slot: 2, name: next, type: Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;}<br>
- {start: 14, end: 67, slot: 0, name: executorLoader, type: Ljava/util/ServiceLoader;}<br>
- {start: 19, end: 67, slot: 1, name: iterator, type: Ljava/util/Iterator;}<br>
local variable types: <br>
- {start: 14, end: 67, slot: 0, name: executorLoader, signature: Ljava/util/ServiceLoader<Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;>;}<br>
- {start: 19, end: 67, slot: 1, name: iterator, signature: Ljava/util/Iterator<Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;>;}<br>
stack map frames: <br>
58: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: []}<br>
61: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: []}<br>
//stack map frame @0: {locals: [], stack: []}<br>
0: {opcode: LDC, constant value: mutiny.disableCallBackDecorators}<br>
2: {opcode: INVOKESTATIC, owner: java/lang/Boolean, method name: getBoolean, method type: (Ljava/lang/String;)Z}<br>
5: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: DISABLE_CALLBACK_DECORATORS, field type: Z}<br>
8: {opcode: LDC, constant value: 'ClassDesc[ExecutorConfiguration]'}<br>
10: {opcode: INVOKESTATIC, owner: java/util/ServiceLoader, method name: load, method type: (Ljava/lang/Class;)Ljava/util/ServiceLoader;}<br>
13: {opcode: ASTORE_0, slot: 0}<br>
14: {opcode: ALOAD_0, slot: 0, type: Ljava/util/ServiceLoader;, variable name: executorLoader}<br>
15: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader, method name: iterator, method type: ()Ljava/util/Iterator;}<br>
18: {opcode: ASTORE_1, slot: 1}<br>
19: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;, variable name: iterator}<br>
20: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name: hasNext, method type: ()Z}<br>
25: {opcode: IFEQ, target: 58}<br>
28: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;, variable name: iterator}<br>
29: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name: next, method type: ()Ljava/lang/Object;}<br>
34: {opcode: CHECKCAST, type: io/smallrye/mutiny/infrastructure/ExecutorConfiguration}<br>
37: {opcode: ASTORE_2, slot: 2}<br>
38: {opcode: ALOAD_2, slot: 2, type: Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;, variable name: next}<br>
39: {opcode: INVOKEINTERFACE, owner: io/smallrye/mutiny/infrastructure/ExecutorConfiguration, method name: getDefaultWorkerExecutor, method type: ()Ljava/util/concurrent/Executor;}<br>
44: {opcode: LDC, constant value: executor}<br>
46: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/helpers/ParameterValidation, method name: nonNull, method type: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;}<br>
49: {opcode: CHECKCAST, type: java/util/concurrent/Executor}<br>
52: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: setDefaultExecutor, method type: (Ljava/util/concurrent/Executor;)V}<br>
55: {opcode: GOTO, target: 61}<br>
//stack map frame @58: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: []}<br>
58: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: setDefaultExecutor, method type: ()V}<br>
//stack map frame @61: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: []}<br>
61: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: reload, method type: ()V}<br>
64: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: resetCanCallerThreadBeBlockedSupplier, method type: ()V}<br>
67: {opcode: GETSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer, field name: INSTANCE, field type: Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer;}<br>
70: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: droppedExceptionHandler, field type: Ljava/util/function/Consumer;}<br>
73: {opcode: GETSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger, field name: INSTANCE, field type: Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger;}<br>
76: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: operatorLogger, field type: Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger;}<br>
79: {opcode: SIPUSH, constant value: 128}<br>
82: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: multiOverflowDefaultBufferSize, field type: I}<br>
85: {opcode: BIPUSH, constant value: 32}<br>
87: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeXs, field type: I}<br>
90: {opcode: SIPUSH, constant value: 256}<br>
93: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeS, field type: I}<br>
96: {opcode: RETURN}</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">Here's the rewritten version with the wrong stack map:</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif"> Current Frame:<br>
bci: @55<br>
flags: { }<br>
locals: { 'java/util/ServiceLoader', 'java/util/Iterator', 'io/smallrye/mutiny/infrastructure/ExecutorConfiguration' }<br>
stack: { }<br>
Stackmap Frame:<br>
bci: @61<br>
flags: { }<br>
locals: { 'java/util/ServiceLoader', 'java/util/Iterator' }<br>
stack: { integer }<br>
Bytecode:<br>
0000000: 1259 b800 5ab3 003b 125b b802 314b 2ab6<br>
0000010: 0040 4c2b b900 4101 0099 0021 2bb9 0042<br>
0000020: 0100 c000 5b4d 2cb9 005c 0100 125d b800<br>
0000030: 30c0 005e b800 09a7 0006 b800 5fb8 0060<br>
0000040: b800 61b2 0047 b300 2db2 0050 b300 4c11<br>
0000050: 0080 b300 0510 20b3 0006 1101 00b3 0007<br>
0000060: b1 <br>
- {start: 14, line number: 43}<br>
- {start: 19, line number: 44}<br>
- {start: 28, line number: 45}<br>
- {start: 38, line number: 46}<br>
- {start: 55, line number: 47}<br>
- {start: 58, line number: 48}<br>
- {start: 61, line number: 51}<br>
- {start: 64, line number: 53}<br>
- {start: 67, line number: 63}<br>
- {start: 73, line number: 65}<br>
- {start: 79, line number: 67}<br>
- {start: 85, line number: 69}<br>
- {start: 90, line number: 70}<br>
stack map frames: <br>
58: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: []}<br>
61: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: [int]}<br>
//stack map frame @0: {locals: [], stack: []}<br>
0: {opcode: LDC, constant value: mutiny.disableCallBackDecorators}<br>
2: {opcode: INVOKESTATIC, owner: java/lang/Boolean, method name: getBoolean, method type: (Ljava/lang/String;)Z}<br>
5: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: DISABLE_CALLBACK_DECORATORS, field type: Z}<br>
8: {opcode: LDC, constant value: 'ClassDesc[ExecutorConfiguration]'}<br>
10: {opcode: INVOKESTATIC, owner: io/github/dmlloyd/modules/ModuleServiceLoader, method name: load, method type: (Ljava/lang/Class;)Ljava/util/ServiceLoader;}<br>
13: {opcode: ASTORE_0, slot: 0}<br>
14: {opcode: ALOAD_0, slot: 0, type: Ljava/util/ServiceLoader;, variable name: executorLoader}<br>
15: {opcode: INVOKEVIRTUAL, owner: java/util/ServiceLoader, method name: iterator, method type: ()Ljava/util/Iterator;}<br>
18: {opcode: ASTORE_1, slot: 1}<br>
19: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;, variable name: iterator}<br>
20: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name: hasNext, method type: ()Z}<br>
25: {opcode: IFEQ, target: 58}<br>
28: {opcode: ALOAD_1, slot: 1, type: Ljava/util/Iterator;, variable name: iterator}<br>
29: {opcode: INVOKEINTERFACE, owner: java/util/Iterator, method name: next, method type: ()Ljava/lang/Object;}<br>
34: {opcode: CHECKCAST, type: io/smallrye/mutiny/infrastructure/ExecutorConfiguration}<br>
37: {opcode: ASTORE_2, slot: 2}<br>
38: {opcode: ALOAD_2, slot: 2, type: Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration;, variable name: next}<br>
39: {opcode: INVOKEINTERFACE, owner: io/smallrye/mutiny/infrastructure/ExecutorConfiguration, method name: getDefaultWorkerExecutor, method type: ()Ljava/util/concurrent/Executor;}<br>
44: {opcode: LDC, constant value: executor}<br>
46: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/helpers/ParameterValidation, method name: nonNull, method type: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;}<br>
49: {opcode: CHECKCAST, type: java/util/concurrent/Executor}<br>
52: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: setDefaultExecutor, method type: (Ljava/util/concurrent/Executor;)V}<br>
55: {opcode: GOTO, target: 61}<br>
//stack map frame @58: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: []}<br>
58: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: setDefaultExecutor, method type: ()V}<br>
//stack map frame @61: {locals: [java/util/ServiceLoader, java/util/Iterator], stack: [int]}<br>
61: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: reload, method type: ()V}<br>
64: {opcode: INVOKESTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, method name: resetCanCallerThreadBeBlockedSupplier, method type: ()V}<br>
67: {opcode: GETSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer, field name: INSTANCE, field type: Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndD<br>
umpThrowableConsumer;}<br>
70: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: droppedExceptionHandler, field type: Ljava/util/function/Consumer;}<br>
73: {opcode: GETSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger, field name: INSTANCE, field type: Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger;}<br>
76: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: operatorLogger, field type: Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger;}<br>
79: {opcode: SIPUSH, constant value: 128}<br>
82: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: multiOverflowDefaultBufferSize, field type: I}<br>
85: {opcode: BIPUSH, constant value: 32}<br>
87: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeXs, field type: I}<br>
90: {opcode: SIPUSH, constant value: 256}<br>
93: {opcode: PUTSTATIC, owner: io/smallrye/mutiny/infrastructure/Infrastructure, field name: bufferSizeS, field type: I}<br>
96: {opcode: RETURN}<br>
</div>
<br clear="all">
</div>
<div>
<div class="x_gmail_default" style="font-family:arial,helvetica,sans-serif">[1] <a href="https://github.com/dmlloyd/jdk-classfile-backport">https://github.com/dmlloyd/jdk-classfile-backport</a></div>
<br>
</div>
<span class="x_gmail_signature_prefix">-- </span><br>
<div dir="ltr" class="x_gmail_signature" data-smartmail="gmail_signature">
<div dir="ltr">- DML • he/him<br>
</div>
</div>
</div>
</div>
</body>
</html>