From liangchenblue at gmail.com Sun Dec 1 14:40:13 2024 From: liangchenblue at gmail.com (Chen Liang) Date: Sun, 1 Dec 2024 08:40:13 -0600 Subject: More last minute changes for the ClassFile API Message-ID: Hello, In the week before RDP 1, I think there are still a few enhancements that require removal of existing APIs: 1. The components package. The utilities in this package are often inconsistent; for example, class name remapper does not remap simple names in InnerClasses; the locals shifter shifts an arbitrary number of slots. These inconsistent behaviors will have to be kept even if they make no sense in the long run, because people might have dependencies on these behaviors. I think we should move these components to example snippets for users, much like the System exit detection example in JEP 486. 2. The return types of PoolEntry::tag and AnnotationValue::tag. They currently return byte and char respectively while their constants are of int type. In addition, the byte tag can overflow if the constant pool tags exceed 127, and by then the default Java widening conversion would be erroneous for the constant pool tags. What do you think? Regards -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Dec 1 16:24:20 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 1 Dec 2024 16:24:20 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: References: Message-ID: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> I have always been uncomfortable with the components package. But it is also pretty late in the cycle. > On Dec 1, 2024, at 9:40?AM, Chen Liang wrote: > > ? > Hello, > In the week before RDP 1, I think there are still a few enhancements that require removal of existing APIs: > 1. The components package. The utilities in this package are often inconsistent; for example, class name remapper does not remap simple names in InnerClasses; the locals shifter shifts an arbitrary number of slots. These inconsistent behaviors will have to be kept even if they make no sense in the long run, because people might have dependencies on these behaviors. I think we should move these components to example snippets for users, much like the System exit detection example in JEP 486. > 2. The return types of PoolEntry::tag and AnnotationValue::tag. They currently return byte and char respectively while their constants are of int type. In addition, the byte tag can overflow if the constant pool tags exceed 127, and by then the default Java widening conversion would be erroneous for the constant pool tags. > > What do you think? > Regards From markro at cs.washington.edu Sun Dec 1 21:35:19 2024 From: markro at cs.washington.edu (Mark Roberts) Date: Sun, 1 Dec 2024 13:35:19 -0800 Subject: jdk.internal.classfile.impl Message-ID: I have found some useful methods in jdk.internal.classfile.impl ? such as LabelImpl::getBCI(). Are there any future plans to reorganize this package such that --add-exports java.base/jdk.internal.classfile.impl=ALL-UNNAMED will no longer work? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From liangchenblue at gmail.com Sun Dec 1 23:03:13 2024 From: liangchenblue at gmail.com (Chen Liang) Date: Sun, 1 Dec 2024 17:03:13 -0600 Subject: More last minute changes for the ClassFile API In-Reply-To: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> Message-ID: Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen On Sun, Dec 1, 2024 at 10:24?AM Brian Goetz wrote: > I have always been uncomfortable with the components package. But it is > also pretty late in the cycle. > > > On Dec 1, 2024, at 9:40?AM, Chen Liang wrote: > > > > ? > > Hello, > > In the week before RDP 1, I think there are still a few enhancements > that require removal of existing APIs: > > 1. The components package. The utilities in this package are often > inconsistent; for example, class name remapper does not remap simple names > in InnerClasses; the locals shifter shifts an arbitrary number of slots. > These inconsistent behaviors will have to be kept even if they make no > sense in the long run, because people might have dependencies on these > behaviors. I think we should move these components to example snippets for > users, much like the System exit detection example in JEP 486. > > 2. The return types of PoolEntry::tag and AnnotationValue::tag. They > currently return byte and char respectively while their constants are of > int type. In addition, the byte tag can overflow if the constant pool tags > exceed 127, and by then the default Java widening conversion would be > erroneous for the constant pool tags. > > > > What do you think? > > Regards > -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Sun Dec 1 23:46:56 2024 From: markro at cs.washington.edu (Mark Roberts) Date: Sun, 1 Dec 2024 15:46:56 -0800 Subject: More last minute changes for the ClassFile API In-Reply-To: References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> Message-ID: <55c0c179c42e3da39a4f3dc3ac69b917@mail.gmail.com> I have found the ClassPrinter to be a useful debugging tool. Mark *From:* classfile-api-dev *On Behalf Of *Chen Liang *Sent:* Sunday, December 1, 2024 3:03 PM *To:* Brian Goetz *Cc:* classfile-api-dev *Subject:* Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen On Sun, Dec 1, 2024 at 10:24?AM Brian Goetz wrote: I have always been uncomfortable with the components package. But it is also pretty late in the cycle. > On Dec 1, 2024, at 9:40?AM, Chen Liang wrote: > > ? > Hello, > In the week before RDP 1, I think there are still a few enhancements that require removal of existing APIs: > 1. The components package. The utilities in this package are often inconsistent; for example, class name remapper does not remap simple names in InnerClasses; the locals shifter shifts an arbitrary number of slots. These inconsistent behaviors will have to be kept even if they make no sense in the long run, because people might have dependencies on these behaviors. I think we should move these components to example snippets for users, much like the System exit detection example in JEP 486. > 2. The return types of PoolEntry::tag and AnnotationValue::tag. They currently return byte and char respectively while their constants are of int type. In addition, the byte tag can overflow if the constant pool tags exceed 127, and by then the default Java widening conversion would be erroneous for the constant pool tags. > > What do you think? > Regards -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Mon Dec 2 07:51:08 2024 From: adam.sotona at oracle.com (Adam Sotona) Date: Mon, 2 Dec 2024 07:51:08 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> Message-ID: ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker are single-purpose components standing on top of Class-File API and they have been carefully selected to support more complex use cases (like for example code instrumentation). I partly agree they may be turned into examples. However, the learning curve will be extremely steep for users forced to implement them first instead of just use them. ClassPrinter is a different beast. This tool raised from serious needs and become significant complement to the API. It has been cleaned and simplified to its bare bones to avoid any confusion about its purpose. Maybe the components package location for ClassPrinter is not ideal however it works to differentiate it from the core API. I have no objections to unify sub-int return types to int. Thanks, Adam From: classfile-api-dev on behalf of Chen Liang Date: Monday, 2 December 2024 at 0:03 To: Brian Goetz Cc: classfile-api-dev Subject: Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Mon Dec 2 16:14:21 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Mon, 2 Dec 2024 16:14:21 +0000 Subject: jdk.internal.classfile.impl In-Reply-To: References: Message-ID: Hi Mark, LabelImpl::getBCI is accessible via CodeAttribute::labelToBci if you read from a class file. The code attribute can be accessed via method.findAttribute(Attributes.CODE).orElseThrow() If you want to use this to access the BCI for Labels in a CodeBuilder, unfortunately, that does not work. There is no guarantee that all Labels are assigned definite BCIs during that Consumer. Regards, Chen ________________________________ From: classfile-api-dev on behalf of Mark Roberts Sent: Sunday, December 1, 2024 3:35 PM To: Chen Liang ; classfile-api-dev Subject: jdk.internal.classfile.impl I have found some useful methods in jdk.internal.classfile.impl ? such as LabelImpl::getBCI(). Are there any future plans to reorganize this package such that --add-exports java.base/jdk.internal.classfile.impl=ALL-UNNAMED will no longer work? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Dec 2 16:31:54 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 2 Dec 2024 16:31:54 +0000 Subject: jdk.internal.classfile.impl In-Reply-To: References: Message-ID: While there are not necessarily such plans now, you should always act as if this will happen at the worst possible time. If you use options to relax encapsulation, you are basically signing a document that says ?I will change my code whenever I have to, and I won?t complain about it.? The tradeoff you are making here is that you are binding tightly to an implementation that has the flexibility to change. You may get a short-term benefit, but you take on a long-term risk. You get to make these choices, and deal with the consequences. On Dec 1, 2024, at 4:35 PM, Mark Roberts > wrote: I have found some useful methods in jdk.internal.classfile.impl ? such as LabelImpl::getBCI(). Are there any future plans to reorganize this package such that --add-exports java.base/jdk.internal.classfile.impl=ALL-UNNAMED will no longer work? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Dec 2 16:50:19 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 2 Dec 2024 16:50:19 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> Message-ID: <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> My main concern with the single-purpose components is that I think they are in the wrong place. Co-locating them with the classfile API feels like it is promising more than we deliver, and users might feel they have to understand what they do. I agree that the printing functionality is essential, and perhaps it doesn?t belong in components, especially if the rest off components is going away. On Dec 2, 2024, at 2:51 AM, Adam Sotona > wrote: ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker are single-purpose components standing on top of Class-File API and they have been carefully selected to support more complex use cases (like for example code instrumentation). I partly agree they may be turned into examples. However, the learning curve will be extremely steep for users forced to implement them first instead of just use them. ClassPrinter is a different beast. This tool raised from serious needs and become significant complement to the API. It has been cleaned and simplified to its bare bones to avoid any confusion about its purpose. Maybe the components package location for ClassPrinter is not ideal however it works to differentiate it from the core API. I have no objections to unify sub-int return types to int. Thanks, Adam From: classfile-api-dev > on behalf of Chen Liang > Date: Monday, 2 December 2024 at 0:03 To: Brian Goetz > Cc: classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Dec 2 17:39:39 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 2 Dec 2024 17:39:39 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> Message-ID: <3c1c92b3-1d99-4c4f-82c3-13affc932961@oracle.com> On 02/12/2024 16:50, Brian Goetz wrote: > My main concern with the single-purpose components is that I think > they are in the wrong place. ?Co-locating them with the classfile API > feels like it is promising more than we deliver, and users might feel > they have to understand what they do. > > I agree that the printing functionality is essential, and perhaps it > doesn?t belong in components, especially if the rest off components is > going away. While I agree that some of the stuff in here seems useful, I'm not sure how much we want to commit to it. Even if I look at ClassPrinter only, there's XML, Yaml and JSON output. These probably use a custom schema, which might, or might not fit the user needs. The fact that we even say: > * Printing is for debugging purposes only. Printed text schema, tree > content and structure * not guaranteed. It may change anytime in a future. Is, IMHO, a big red herring. I think having a way to dump a class model in a textual form is a good thing (but I'd say that something like this can be achieved by adding some method to ClassModel, rather than having a separate component?). But the stuff in this package seems relatively niche-y, and we'd be perhaps better off to leave this out, at least for now. If, in the future, we think that some of the components are sufficiently general to be added to the API, we can consider it? (e.g. we don't have to decide now?) Cheers Maurizio > > > >> On Dec 2, 2024, at 2:51 AM, Adam Sotona wrote: >> >> ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker >> are single-purpose components standing on top of Class-File API and >> they have been carefully selected to support more complex use cases >> (like for example code instrumentation). I partly agree they may be >> turned into examples. However, the learning curve will be extremely >> steep for users forced to implement them first instead of just use them. >> ClassPrinter is a different beast. This tool raised from serious >> needs and become significant complement to the API. It has been >> cleaned and simplified to its bare bones to avoid any confusion about >> its purpose. Maybe the components package location for ClassPrinter >> is not ideal however it works to differentiate it from the core API. >> I have no objections to unify sub-int return types to int. >> Thanks, >> Adam >> >> *From:*classfile-api-dev on >> behalf of Chen Liang >> *Date:*Monday, 2 December 2024 at 0:03 >> *To:*Brian Goetz >> *Cc:*classfile-api-dev >> *Subject:*Re: More last minute changes for the ClassFile API >> >> Hmm, for components package, I did a quick rundown ingrep.app >> search (previously used for terminally deprecating >> ZipError) and a search within JDK source: >> 1. A search ongrep.app shows there are no non-JDK >> usages of the components package; in contrast, all other classfile >> packages (attribute, instruction, constantpool) and the >> java.lang.constant package have usages beyond the JDK. A search on >> GitHub shows there is?no actual usages of components package, yet >> ClassPrinter.ListNode is frequently used in imports due to the name >> similarity with a class offered by Leetcode. >> 2. A search within the JDK itself shows there is no use of these >> components, besides ClassPrinter in JFR, outside of ClassFile API and >> its tests. >> I think the removal at this point is safe; even for the ClassPrinter, >> its huge amount of data exposed is very prone to unintended >> dependencies, which can turn it into a monolith that we cannot >> adjust?at all later down the road. >> Also for the 2 return types: Is it fine for us to promote the return >> types to `int` to promote consistency with the constant fields? >> Chen > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Dec 2 17:42:45 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 2 Dec 2024 17:42:45 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: <3c1c92b3-1d99-4c4f-82c3-13affc932961@oracle.com> References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> <3c1c92b3-1d99-4c4f-82c3-13affc932961@oracle.com> Message-ID: If there are no JDK dependencies on the components package, we can propose to drop the entirety from 24 and bring back the relevant bits in 25. That?s a simple CSR. On Dec 2, 2024, at 12:39 PM, Maurizio Cimadamore > wrote: On 02/12/2024 16:50, Brian Goetz wrote: My main concern with the single-purpose components is that I think they are in the wrong place. Co-locating them with the classfile API feels like it is promising more than we deliver, and users might feel they have to understand what they do. I agree that the printing functionality is essential, and perhaps it doesn?t belong in components, especially if the rest off components is going away. While I agree that some of the stuff in here seems useful, I'm not sure how much we want to commit to it. Even if I look at ClassPrinter only, there's XML, Yaml and JSON output. These probably use a custom schema, which might, or might not fit the user needs. The fact that we even say: * Printing is for debugging purposes only. Printed text schema, tree content and structure * not guaranteed. It may change anytime in a future. Is, IMHO, a big red herring. I think having a way to dump a class model in a textual form is a good thing (but I'd say that something like this can be achieved by adding some method to ClassModel, rather than having a separate component?). But the stuff in this package seems relatively niche-y, and we'd be perhaps better off to leave this out, at least for now. If, in the future, we think that some of the components are sufficiently general to be added to the API, we can consider it? (e.g. we don't have to decide now?) Cheers Maurizio On Dec 2, 2024, at 2:51 AM, Adam Sotona > wrote: ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker are single-purpose components standing on top of Class-File API and they have been carefully selected to support more complex use cases (like for example code instrumentation). I partly agree they may be turned into examples. However, the learning curve will be extremely steep for users forced to implement them first instead of just use them. ClassPrinter is a different beast. This tool raised from serious needs and become significant complement to the API. It has been cleaned and simplified to its bare bones to avoid any confusion about its purpose. Maybe the components package location for ClassPrinter is not ideal however it works to differentiate it from the core API. I have no objections to unify sub-int return types to int. Thanks, Adam From: classfile-api-dev > on behalf of Chen Liang > Date: Monday, 2 December 2024 at 0:03 To: Brian Goetz > Cc: classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Mon Dec 2 17:59:46 2024 From: adam.sotona at oracle.com (Adam Sotona) Date: Mon, 2 Dec 2024 17:59:46 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> <3c1c92b3-1d99-4c4f-82c3-13affc932961@oracle.com> Message-ID: I strongly disagree with removal of ClassPrinter, it become an essential tool. We refined its design in the early prototypes, introduced it in two previews. Adding print methods to ClassModel has been already discussed and denied. From: Brian Goetz Date: Monday, 2 December 2024 at 18:42 To: Maurizio Cimadamore Cc: Adam Sotona , Chen Liang , classfile-api-dev Subject: Re: More last minute changes for the ClassFile API If there are no JDK dependencies on the components package, we can propose to drop the entirety from 24 and bring back the relevant bits in 25. That?s a simple CSR. On Dec 2, 2024, at 12:39 PM, Maurizio Cimadamore > wrote: On 02/12/2024 16:50, Brian Goetz wrote: My main concern with the single-purpose components is that I think they are in the wrong place. Co-locating them with the classfile API feels like it is promising more than we deliver, and users might feel they have to understand what they do. I agree that the printing functionality is essential, and perhaps it doesn?t belong in components, especially if the rest off components is going away. While I agree that some of the stuff in here seems useful, I'm not sure how much we want to commit to it. Even if I look at ClassPrinter only, there's XML, Yaml and JSON output. These probably use a custom schema, which might, or might not fit the user needs. The fact that we even say: * Printing is for debugging purposes only. Printed text schema, tree content and structure * not guaranteed. It may change anytime in a future. Is, IMHO, a big red herring. I think having a way to dump a class model in a textual form is a good thing (but I'd say that something like this can be achieved by adding some method to ClassModel, rather than having a separate component?). But the stuff in this package seems relatively niche-y, and we'd be perhaps better off to leave this out, at least for now. If, in the future, we think that some of the components are sufficiently general to be added to the API, we can consider it? (e.g. we don't have to decide now?) Cheers Maurizio On Dec 2, 2024, at 2:51 AM, Adam Sotona > wrote: ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker are single-purpose components standing on top of Class-File API and they have been carefully selected to support more complex use cases (like for example code instrumentation). I partly agree they may be turned into examples. However, the learning curve will be extremely steep for users forced to implement them first instead of just use them. ClassPrinter is a different beast. This tool raised from serious needs and become significant complement to the API. It has been cleaned and simplified to its bare bones to avoid any confusion about its purpose. Maybe the components package location for ClassPrinter is not ideal however it works to differentiate it from the core API. I have no objections to unify sub-int return types to int. Thanks, Adam From: classfile-api-dev > on behalf of Chen Liang > Date: Monday, 2 December 2024 at 0:03 To: Brian Goetz > Cc: classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Dec 2 18:03:38 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 2 Dec 2024 18:03:38 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> <3c1c92b3-1d99-4c4f-82c3-13affc932961@oracle.com> Message-ID: <015EAB2E-B6FA-4FF7-82F5-C8A0421BE243@oracle.com> My problem is not with ClassPrinter, but with the concept of a `components` package in general. The ones we have don?t really justify themselves, and it risks becoming either a dumping ground or a maintenance problem. Perhaps moving ClassPrinter to the tests for the time being while we find a better place and API for it is a reasonable short-term move? On Dec 2, 2024, at 12:59 PM, Adam Sotona > wrote: I strongly disagree with removal of ClassPrinter, it become an essential tool. We refined its design in the early prototypes, introduced it in two previews. Adding print methods to ClassModel has been already discussed and denied. From: Brian Goetz > Date: Monday, 2 December 2024 at 18:42 To: Maurizio Cimadamore > Cc: Adam Sotona >, Chen Liang >, classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API If there are no JDK dependencies on the components package, we can propose to drop the entirety from 24 and bring back the relevant bits in 25. That?s a simple CSR. On Dec 2, 2024, at 12:39 PM, Maurizio Cimadamore > wrote: On 02/12/2024 16:50, Brian Goetz wrote: My main concern with the single-purpose components is that I think they are in the wrong place. Co-locating them with the classfile API feels like it is promising more than we deliver, and users might feel they have to understand what they do. I agree that the printing functionality is essential, and perhaps it doesn?t belong in components, especially if the rest off components is going away. While I agree that some of the stuff in here seems useful, I'm not sure how much we want to commit to it. Even if I look at ClassPrinter only, there's XML, Yaml and JSON output. These probably use a custom schema, which might, or might not fit the user needs. The fact that we even say: * Printing is for debugging purposes only. Printed text schema, tree content and structure * not guaranteed. It may change anytime in a future. Is, IMHO, a big red herring. I think having a way to dump a class model in a textual form is a good thing (but I'd say that something like this can be achieved by adding some method to ClassModel, rather than having a separate component?). But the stuff in this package seems relatively niche-y, and we'd be perhaps better off to leave this out, at least for now. If, in the future, we think that some of the components are sufficiently general to be added to the API, we can consider it? (e.g. we don't have to decide now?) Cheers Maurizio On Dec 2, 2024, at 2:51 AM, Adam Sotona > wrote: ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker are single-purpose components standing on top of Class-File API and they have been carefully selected to support more complex use cases (like for example code instrumentation). I partly agree they may be turned into examples. However, the learning curve will be extremely steep for users forced to implement them first instead of just use them. ClassPrinter is a different beast. This tool raised from serious needs and become significant complement to the API. It has been cleaned and simplified to its bare bones to avoid any confusion about its purpose. Maybe the components package location for ClassPrinter is not ideal however it works to differentiate it from the core API. I have no objections to unify sub-int return types to int. Thanks, Adam From: classfile-api-dev > on behalf of Chen Liang > Date: Monday, 2 December 2024 at 0:03 To: Brian Goetz > Cc: classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Mon Dec 2 18:53:11 2024 From: markro at cs.washington.edu (Mark Roberts) Date: Mon, 2 Dec 2024 10:53:11 -0800 Subject: LocalVariableTableAttribute Message-ID: First, a more general question about attributes. There are a number of these items that are not directly exposed as a MethodELement or a CodeElement. How and when do these attributes get ?built?, i.e., copied to the output class? The LocalVariableTable needs to be modified as part of our instrumentation. I see how to build a LocalVariableTalbeAttribute with the ?of()? method, but how do I replace the existing table? Also, what is the relationship between the LocalVariableTable and the LocalVariable CodeElement? Are they duplicate information? Or perhaps one is derived from the other? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Mon Dec 2 19:14:46 2024 From: adam.sotona at oracle.com (Adam Sotona) Date: Mon, 2 Dec 2024 19:14:46 +0000 Subject: More last minute changes for the ClassFile API In-Reply-To: <015EAB2E-B6FA-4FF7-82F5-C8A0421BE243@oracle.com> References: <9AE0FCCB-2308-4226-B452-5AA4EB9DC5AB@oracle.com> <23F712D7-0826-49E0-9AFC-2081DF5A25E4@oracle.com> <3c1c92b3-1d99-4c4f-82c3-13affc932961@oracle.com> <015EAB2E-B6FA-4FF7-82F5-C8A0421BE243@oracle.com> Message-ID: First a technical note: ClassPrinter is essential part of the Class-File API implementation, and at least its implementation cannot be moved out of the java.base module. Four years ago, we spent significant time on the ClassPrinter design, location, and format. In the current form we?ve released it in two JDK previews and ClassPrinter received positive feedback. Now, when the Class-File API has been finalized, why should we nuke it and return to the drawing board? I?m missing the purpose of all the previews, feedbacks and API reviews. From: Brian Goetz Date: Monday, 2 December 2024 at 19:03 To: Adam Sotona Cc: Maurizio Cimadamore , Chen Liang , classfile-api-dev Subject: Re: More last minute changes for the ClassFile API My problem is not with ClassPrinter, but with the concept of a `components` package in general. The ones we have don?t really justify themselves, and it risks becoming either a dumping ground or a maintenance problem. Perhaps moving ClassPrinter to the tests for the time being while we find a better place and API for it is a reasonable short-term move? On Dec 2, 2024, at 12:59 PM, Adam Sotona > wrote: I strongly disagree with removal of ClassPrinter, it become an essential tool. We refined its design in the early prototypes, introduced it in two previews. Adding print methods to ClassModel has been already discussed and denied. From: Brian Goetz > Date: Monday, 2 December 2024 at 18:42 To: Maurizio Cimadamore > Cc: Adam Sotona >, Chen Liang >, classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API If there are no JDK dependencies on the components package, we can propose to drop the entirety from 24 and bring back the relevant bits in 25. That?s a simple CSR. On Dec 2, 2024, at 12:39 PM, Maurizio Cimadamore > wrote: On 02/12/2024 16:50, Brian Goetz wrote: My main concern with the single-purpose components is that I think they are in the wrong place. Co-locating them with the classfile API feels like it is promising more than we deliver, and users might feel they have to understand what they do. I agree that the printing functionality is essential, and perhaps it doesn?t belong in components, especially if the rest off components is going away. While I agree that some of the stuff in here seems useful, I'm not sure how much we want to commit to it. Even if I look at ClassPrinter only, there's XML, Yaml and JSON output. These probably use a custom schema, which might, or might not fit the user needs. The fact that we even say: * Printing is for debugging purposes only. Printed text schema, tree content and structure * not guaranteed. It may change anytime in a future. Is, IMHO, a big red herring. I think having a way to dump a class model in a textual form is a good thing (but I'd say that something like this can be achieved by adding some method to ClassModel, rather than having a separate component?). But the stuff in this package seems relatively niche-y, and we'd be perhaps better off to leave this out, at least for now. If, in the future, we think that some of the components are sufficiently general to be added to the API, we can consider it? (e.g. we don't have to decide now?) Cheers Maurizio On Dec 2, 2024, at 2:51 AM, Adam Sotona > wrote: ClassRemapper, CodeLocalsShifter, CodeRelabeler and CodeStackTracker are single-purpose components standing on top of Class-File API and they have been carefully selected to support more complex use cases (like for example code instrumentation). I partly agree they may be turned into examples. However, the learning curve will be extremely steep for users forced to implement them first instead of just use them. ClassPrinter is a different beast. This tool raised from serious needs and become significant complement to the API. It has been cleaned and simplified to its bare bones to avoid any confusion about its purpose. Maybe the components package location for ClassPrinter is not ideal however it works to differentiate it from the core API. I have no objections to unify sub-int return types to int. Thanks, Adam From: classfile-api-dev > on behalf of Chen Liang > Date: Monday, 2 December 2024 at 0:03 To: Brian Goetz > Cc: classfile-api-dev > Subject: Re: More last minute changes for the ClassFile API Hmm, for components package, I did a quick rundown in grep.app search (previously used for terminally deprecating ZipError) and a search within JDK source: 1. A search on grep.app shows there are no non-JDK usages of the components package; in contrast, all other classfile packages (attribute, instruction, constantpool) and the java.lang.constant package have usages beyond the JDK. A search on GitHub shows there is no actual usages of components package, yet ClassPrinter.ListNode is frequently used in imports due to the name similarity with a class offered by Leetcode. 2. A search within the JDK itself shows there is no use of these components, besides ClassPrinter in JFR, outside of ClassFile API and its tests. I think the removal at this point is safe; even for the ClassPrinter, its huge amount of data exposed is very prone to unintended dependencies, which can turn it into a monolith that we cannot adjust at all later down the road. Also for the 2 return types: Is it fine for us to promote the return types to `int` to promote consistency with the constant fields? Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Mon Dec 2 20:09:38 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Mon, 2 Dec 2024 20:09:38 +0000 Subject: LocalVariableTableAttribute In-Reply-To: References: Message-ID: Hi Mark, Some attributes do not extend MethodElement: this is notably the case for containers of some Class-File pseudo instructions, such as LocalVariable, LocalVariableType, and CharacterRange. You can check the sealed class hierarchy of PseudoInstruction to discover all of them. Indeed, it seems you cannot provide LocalVariableTableAttribute to any class file builder; however, you should be able to discover it from a CodeAttribute when you do a code.findAttribute(Attributes.localVariableTable()). It is supposed to be a code-independent representation, as it has no Label references but use raw BCI instead. So LocalVariable is derived from LocalVariableTableAttribute. The only way to construct a LocalVariableTable is to do CodeBuilder::localVariable() when you build the code, I think. Note that when you have a CodeTransform, the accept(CodeBuilder, CodeElement) may receive a LocalVariable as a CodeElement by default, and you can choose to reuse that local variable, drop it, or call the builder method to create a transformed variable. Regards, Chen ________________________________ From: classfile-api-dev on behalf of Mark Roberts Sent: Monday, December 2, 2024 12:53 PM To: classfile-api-dev Cc: Chen Liang Subject: LocalVariableTableAttribute First, a more general question about attributes. There are a number of these items that are not directly exposed as a MethodELement or a CodeElement. How and when do these attributes get ?built?, i.e., copied to the output class? The LocalVariableTable needs to be modified as part of our instrumentation. I see how to build a LocalVariableTalbeAttribute with the ?of()? method, but how do I replace the existing table? Also, what is the relationship between the LocalVariableTable and the LocalVariable CodeElement? Are they duplicate information? Or perhaps one is derived from the other? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Tue Dec 3 07:08:30 2024 From: adam.sotona at oracle.com (Adam Sotona) Date: Tue, 3 Dec 2024 07:08:30 +0000 Subject: CodeLocalsShifter In-Reply-To: <072caeb1878a1f716ca8c48fad15d30c@mail.gmail.com> References: <072caeb1878a1f716ca8c48fad15d30c@mail.gmail.com> Message-ID: Hi Mark, CodeLocalsShifter shifts all locals passing through it to a newly allocated slots, except for the receiver and method parameters slots (which are protected). Its purpose is to allow more complex (multi-source) transformation, as shown for example here: https://download.java.net/java/early_access/jdk24/docs/api/java.base/java/lang/classfile/components/package-summary.html#class-instrumentation-sample-heading When you have two sources of code (`instrumentor` and `target` in the example), you have to avoid collisions of the locals slots. In the example the instrumentor code precedes the injected target code, so the target code must be re-allocated to the new locals slots beyond the instrumentor code (however method parameter slots must stay unchanged). It should be no problem to add a new CodeLocalsShifter factory method, where you can specify the number of protected slots. Current factory calculates the protected slots from the method parameters, so it is more convenient for the intended use. Feel free to ask if you have any further questions. Thank you, Adam From: classfile-api-dev on behalf of Mark Roberts Date: Saturday, 30 November 2024 at 0:54 To: Chen Liang , classfile-api-dev at openjdk.org Subject: CodeLocalsShifter CodeLocalsShifter seems to shift the locals down a variable number based on the number of current locals. How do I shift the locals down a fixed number that I designate? Thanks, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Mon Dec 9 10:32:55 2024 From: adam.sotona at oracle.com (Adam Sotona) Date: Mon, 9 Dec 2024 10:32:55 +0000 Subject: Class-File API debug printing capability Message-ID: Class-File API lost debug printing capability with removal of the ClassPrinter in JDK-8345343. I?ve created a new enhancement to add a method to `j.l.classfile.CompoundElement` returning its debug printout. It is a chance to get this enhancement in Late-Enhancement Request Process (according to JEP 3) into JDK 24. Please review the CSR: https://bugs.openjdk.org/browse/JDK-8345774 And PR: https://github.com/openjdk/jdk/pull/22634 Thank you! Adam -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Mon Dec 9 15:05:18 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Mon, 9 Dec 2024 15:05:18 +0000 Subject: Class-File API debug printing capability In-Reply-To: References: Message-ID: Thanks for this prompt. Note that CompoundElement implementations already have basic debug printout like ClassModel[thisClass=xxx, flags=xxx], so using "debug" to mean this resulting string is more detailed makes sense. Chen ________________________________ From: classfile-api-dev on behalf of Adam Sotona Sent: Monday, December 9, 2024 4:32 AM To: classfile-api-dev at openjdk.org Subject: Class-File API debug printing capability Class-File API lost debug printing capability with removal of the ClassPrinter in JDK-8345343. I?ve created a new enhancement to add a method to `j.l.classfile.CompoundElement` returning its debug printout. It is a chance to get this enhancement in Late-Enhancement Request Process (according to JEP 3) into JDK 24. Please review the CSR: https://bugs.openjdk.org/browse/JDK-8345774 And PR: https://github.com/openjdk/jdk/pull/22634 Thank you! Adam -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.lloyd at redhat.com Thu Dec 12 17:11:51 2024 From: david.lloyd at redhat.com (David Lloyd) Date: Thu, 12 Dec 2024 11:11:51 -0600 Subject: Stack map generation problem Message-ID: 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;} - {start: 19, end: 67, slot: 1, name: iterator, signature: Ljava/util/Iterator;} 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 -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Thu Dec 12 19:32:37 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Thu, 12 Dec 2024 19:32:37 +0000 Subject: Stack map generation problem In-Reply-To: References: Message-ID: Hmm, couldn't reproduce with a local JDK mainline build (25) and the latest smallrye mutiny 2.7.0. (It also appears from bci that your example is an older version of mutiny). I changed the class java/util/ServiceLoader to Dummy in jshell and cannot reproduce this issue; cannot reproduce with stack maps recomputation either. Here's the javap output of the transformed results. static {}; descriptor: ()V flags: (0x0008) ACC_STATIC Code: stack=2, locals=3, args_size=0 0: ldc_w #304 // String mutiny.disableCallBackDecorators 3: invokestatic #306 // Method java/lang/Boolean.getBoolean:(Ljava/lang/String;)Z 6: putstatic #203 // Field DISABLE_CALLBACK_DECORATORS:Z 9: ldc_w #312 // class io/smallrye/mutiny/infrastructure/ExecutorConfiguration 12: invokestatic #561 // Method Dummy.load:(Ljava/lang/Class;)Ljava/util/ServiceLoader; 15: astore_0 16: aload_0 17: invokevirtual #212 // Method java/util/ServiceLoader.iterator:()Ljava/util/Iterator; 20: astore_1 21: aload_1 22: invokeinterface #216, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 27: ifeq 61 30: aload_1 31: invokeinterface #221, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 36: checkcast #312 // class io/smallrye/mutiny/infrastructure/ExecutorConfiguration 39: astore_2 40: aload_2 41: invokeinterface #314, 1 // InterfaceMethod io/smallrye/mutiny/infrastructure/ExecutorConfiguration.getDefaultWorkerExecutor:()Ljava/util/concurrent/Executor; 46: ldc_w #318 // String executor 49: invokestatic #167 // Method io/smallrye/mutiny/helpers/ParameterValidation.nonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; 52: checkcast #320 // class java/util/concurrent/Executor 55: invokestatic #32 // Method setDefaultExecutor:(Ljava/util/concurrent/Executor;)V 58: goto 64 61: invokestatic #322 // Method setDefaultExecutor:()V 64: invokestatic #324 // Method reload:()V 67: invokestatic #327 // Method resetCanCallerThreadBeBlockedSupplier:()V 70: getstatic #241 // Field io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer.INSTANCE:Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer; 73: putstatic #155 // Field droppedExceptionHandler:Ljava/util/function/Consumer; 76: getstatic #271 // Field io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger.INSTANCE:Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger; 79: putstatic #260 // Field operatorLogger:Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger; 82: sipush 128 85: putstatic #16 // Field multiOverflowDefaultBufferSize:I 88: bipush 32 90: putstatic #20 // Field bufferSizeXs:I 93: sipush 256 96: putstatic #23 // Field bufferSizeS:I 99: return LocalVariableTable: Start Length Slot Name Signature 40 18 2 next Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration; 16 54 0 executorLoader Ljava/util/ServiceLoader; 21 49 1 iterator Ljava/util/Iterator; LocalVariableTypeTable: Start Length Slot Name Signature 16 54 0 executorLoader Ljava/util/ServiceLoader; 21 49 1 iterator Ljava/util/Iterator; LineNumberTable: line 39: 0 line 42: 9 line 43: 16 line 44: 21 line 45: 30 line 46: 40 line 47: 58 line 48: 61 line 51: 64 line 53: 67 line 63: 70 line 65: 76 line 67: 82 line 69: 88 line 70: 93 StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 61 locals = [ class java/util/ServiceLoader, class java/util/Iterator ] frame_type = 2 /* same */ Regards, Chen -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.lloyd at redhat.com Thu Dec 12 19:42:19 2024 From: david.lloyd at redhat.com (David Lloyd) Date: Thu, 12 Dec 2024 13:42:19 -0600 Subject: Stack map generation problem In-Reply-To: References: Message-ID: Hmm. I will try to come up with a more concise reproducer. Maybe it's a problem with my class hierarchy resolver, or some other configuration that I have locally. BTW the example class is from io.smallrye.reactive:mutiny:2.6.2, sorry I failed to specify that. Thanks for looking into it, I'll see if I can make a minimal reproducer. On Thu, Dec 12, 2024 at 1:32?PM Chen Liang wrote: > Hmm, couldn't reproduce with a local JDK mainline build (25) and the > latest smallrye mutiny 2.7.0. (It also appears from bci that your example > is an older version of mutiny). I changed the class java/util/ServiceLoader > to Dummy in jshell and cannot reproduce this issue; cannot reproduce with > stack maps recomputation either. Here's the javap output of the transformed > results. > > static {}; > descriptor: ()V > flags: (0x0008) ACC_STATIC > Code: > stack=2, locals=3, args_size=0 > 0: ldc_w #304 // String > mutiny.disableCallBackDecorators > 3: invokestatic #306 // Method > java/lang/Boolean.getBoolean:(Ljava/lang/String;)Z > 6: putstatic #203 // Field > DISABLE_CALLBACK_DECORATORS:Z > 9: ldc_w #312 // class > io/smallrye/mutiny/infrastructure/ExecutorConfiguration > 12: invokestatic #561 // Method > Dummy.load:(Ljava/lang/Class;)Ljava/util/ServiceLoader; > 15: astore_0 > 16: aload_0 > 17: invokevirtual #212 // Method > java/util/ServiceLoader.iterator:()Ljava/util/Iterator; > 20: astore_1 > 21: aload_1 > 22: invokeinterface #216, 1 // InterfaceMethod > java/util/Iterator.hasNext:()Z > 27: ifeq 61 > 30: aload_1 > 31: invokeinterface #221, 1 // InterfaceMethod > java/util/Iterator.next:()Ljava/lang/Object; > 36: checkcast #312 // class > io/smallrye/mutiny/infrastructure/ExecutorConfiguration > 39: astore_2 > 40: aload_2 > 41: invokeinterface #314, 1 // InterfaceMethod > io/smallrye/mutiny/infrastructure/ExecutorConfiguration.getDefaultWorkerExecutor:()Ljava/util/concurrent/Executor; > 46: ldc_w #318 // String executor > 49: invokestatic #167 // Method > io/smallrye/mutiny/helpers/ParameterValidation.nonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; > 52: checkcast #320 // class > java/util/concurrent/Executor > 55: invokestatic #32 // Method > setDefaultExecutor:(Ljava/util/concurrent/Executor;)V > 58: goto 64 > 61: invokestatic #322 // Method > setDefaultExecutor:()V > 64: invokestatic #324 // Method reload:()V > 67: invokestatic #327 // Method > resetCanCallerThreadBeBlockedSupplier:()V > 70: getstatic #241 // Field > io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer.INSTANCE:Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer; > 73: putstatic #155 // Field > droppedExceptionHandler:Ljava/util/function/Consumer; > 76: getstatic #271 // Field > io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger.INSTANCE:Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger; > 79: putstatic #260 // Field > operatorLogger:Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger; > 82: sipush 128 > 85: putstatic #16 // Field > multiOverflowDefaultBufferSize:I > 88: bipush 32 > 90: putstatic #20 // Field bufferSizeXs:I > 93: sipush 256 > 96: putstatic #23 // Field bufferSizeS:I > 99: return > LocalVariableTable: > Start Length Slot Name Signature > 40 18 2 next > Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration; > 16 54 0 executorLoader Ljava/util/ServiceLoader; > 21 49 1 iterator Ljava/util/Iterator; > LocalVariableTypeTable: > Start Length Slot Name Signature > 16 54 0 executorLoader > Ljava/util/ServiceLoader; > 21 49 1 iterator > Ljava/util/Iterator; > LineNumberTable: > line 39: 0 > line 42: 9 > line 43: 16 > line 44: 21 > line 45: 30 > line 46: 40 > line 47: 58 > line 48: 61 > line 51: 64 > line 53: 67 > line 63: 70 > line 65: 76 > line 67: 82 > line 69: 88 > line 70: 93 > StackMapTable: number_of_entries = 2 > frame_type = 253 /* append */ > offset_delta = 61 > locals = [ class java/util/ServiceLoader, class > java/util/Iterator ] > frame_type = 2 /* same */ > > Regards, Chen > > -- - DML ? he/him -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.lloyd at redhat.com Thu Dec 12 19:08:56 2024 From: david.lloyd at redhat.com (David Lloyd) Date: Thu, 12 Dec 2024 13:08:56 -0600 Subject: Stack map generation problem In-Reply-To: References: Message-ID: 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: , 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: , 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 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;} > - {start: 19, end: 67, slot: 1, name: iterator, signature: > Ljava/util/Iterator;} > 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: From chen.l.liang at oracle.com Thu Dec 12 19:12:36 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Thu, 12 Dec 2024 19:12:36 +0000 Subject: Stack map generation problem In-Reply-To: References: Message-ID: Hi David, 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? 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. Is this the problematic code that broke? https://github.com/smallrye/smallrye-mutiny/blob/fa30c10e3e6fb1ca372b923d4ebe20b07433633c/implementation/src/main/java/io/smallrye/mutiny/infrastructure/Infrastructure.java#L41-L54 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. Regards, Chen Liang ________________________________ From: classfile-api-dev on behalf of David Lloyd Sent: Thursday, December 12, 2024 11:11 AM To: classfile-api-dev at openjdk.org Subject: Stack map generation problem 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;} - {start: 19, end: 67, slot: 1, name: iterator, signature: Ljava/util/Iterator;} 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 -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.lloyd at redhat.com Fri Dec 13 22:31:22 2024 From: david.lloyd at redhat.com (David Lloyd) Date: Fri, 13 Dec 2024 16:31:22 -0600 Subject: Stack map generation problem In-Reply-To: References: Message-ID: I found the problem, and it is not specific to the backport per se: there are several places where `==` is used to compare `ClassDesc` instances, particularly in the stack map generator. This usually works on JDK 24 or later, but fails on 23 or earlier. The critical clue was when I accidentally ran the backport version of the test on JDK 24 and it passed - telling me that the problem was a JDK behavioral difference. On Thu, Dec 12, 2024 at 1:42?PM David Lloyd wrote: > Hmm. I will try to come up with a more concise reproducer. Maybe it's a > problem with my class hierarchy resolver, or some other configuration that > I have locally. > > BTW the example class is from io.smallrye.reactive:mutiny:2.6.2, sorry I > failed to specify that. Thanks for looking into it, I'll see if I can make > a minimal reproducer. > > On Thu, Dec 12, 2024 at 1:32?PM Chen Liang > wrote: > >> Hmm, couldn't reproduce with a local JDK mainline build (25) and the >> latest smallrye mutiny 2.7.0. (It also appears from bci that your example >> is an older version of mutiny). I changed the class java/util/ServiceLoader >> to Dummy in jshell and cannot reproduce this issue; cannot reproduce with >> stack maps recomputation either. Here's the javap output of the transformed >> results. >> >> static {}; >> descriptor: ()V >> flags: (0x0008) ACC_STATIC >> Code: >> stack=2, locals=3, args_size=0 >> 0: ldc_w #304 // String >> mutiny.disableCallBackDecorators >> 3: invokestatic #306 // Method >> java/lang/Boolean.getBoolean:(Ljava/lang/String;)Z >> 6: putstatic #203 // Field >> DISABLE_CALLBACK_DECORATORS:Z >> 9: ldc_w #312 // class >> io/smallrye/mutiny/infrastructure/ExecutorConfiguration >> 12: invokestatic #561 // Method >> Dummy.load:(Ljava/lang/Class;)Ljava/util/ServiceLoader; >> 15: astore_0 >> 16: aload_0 >> 17: invokevirtual #212 // Method >> java/util/ServiceLoader.iterator:()Ljava/util/Iterator; >> 20: astore_1 >> 21: aload_1 >> 22: invokeinterface #216, 1 // InterfaceMethod >> java/util/Iterator.hasNext:()Z >> 27: ifeq 61 >> 30: aload_1 >> 31: invokeinterface #221, 1 // InterfaceMethod >> java/util/Iterator.next:()Ljava/lang/Object; >> 36: checkcast #312 // class >> io/smallrye/mutiny/infrastructure/ExecutorConfiguration >> 39: astore_2 >> 40: aload_2 >> 41: invokeinterface #314, 1 // InterfaceMethod >> io/smallrye/mutiny/infrastructure/ExecutorConfiguration.getDefaultWorkerExecutor:()Ljava/util/concurrent/Executor; >> 46: ldc_w #318 // String executor >> 49: invokestatic #167 // Method >> io/smallrye/mutiny/helpers/ParameterValidation.nonNull:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; >> 52: checkcast #320 // class >> java/util/concurrent/Executor >> 55: invokestatic #32 // Method >> setDefaultExecutor:(Ljava/util/concurrent/Executor;)V >> 58: goto 64 >> 61: invokestatic #322 // Method >> setDefaultExecutor:()V >> 64: invokestatic #324 // Method reload:()V >> 67: invokestatic #327 // Method >> resetCanCallerThreadBeBlockedSupplier:()V >> 70: getstatic #241 // Field >> io/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer.INSTANCE:Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintAndDumpThrowableConsumer; >> 73: putstatic #155 // Field >> droppedExceptionHandler:Ljava/util/function/Consumer; >> 76: getstatic #271 // Field >> io/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger.INSTANCE:Lio/smallrye/mutiny/infrastructure/Infrastructure$PrintOperatorEventOperatorLogger; >> 79: putstatic #260 // Field >> operatorLogger:Lio/smallrye/mutiny/infrastructure/Infrastructure$OperatorLogger; >> 82: sipush 128 >> 85: putstatic #16 // Field >> multiOverflowDefaultBufferSize:I >> 88: bipush 32 >> 90: putstatic #20 // Field bufferSizeXs:I >> 93: sipush 256 >> 96: putstatic #23 // Field bufferSizeS:I >> 99: return >> LocalVariableTable: >> Start Length Slot Name Signature >> 40 18 2 next >> Lio/smallrye/mutiny/infrastructure/ExecutorConfiguration; >> 16 54 0 executorLoader Ljava/util/ServiceLoader; >> 21 49 1 iterator Ljava/util/Iterator; >> LocalVariableTypeTable: >> Start Length Slot Name Signature >> 16 54 0 executorLoader >> Ljava/util/ServiceLoader; >> 21 49 1 iterator >> Ljava/util/Iterator; >> LineNumberTable: >> line 39: 0 >> line 42: 9 >> line 43: 16 >> line 44: 21 >> line 45: 30 >> line 46: 40 >> line 47: 58 >> line 48: 61 >> line 51: 64 >> line 53: 67 >> line 63: 70 >> line 65: 76 >> line 67: 82 >> line 69: 88 >> line 70: 93 >> StackMapTable: number_of_entries = 2 >> frame_type = 253 /* append */ >> offset_delta = 61 >> locals = [ class java/util/ServiceLoader, class >> java/util/Iterator ] >> frame_type = 2 /* same */ >> >> Regards, Chen >> >> > > -- > - DML ? he/him > -- - DML ? he/him -------------- next part -------------- An HTML attachment was scrubbed... URL: From liangchenblue at gmail.com Sat Dec 14 03:01:48 2024 From: liangchenblue at gmail.com (Chen Liang) Date: Fri, 13 Dec 2024 19:01:48 -0800 Subject: Stack map generation problem In-Reply-To: References: Message-ID: Indeed - now primitive class descs are singletons. The whole constant API implementations have been significantly changed during the optimizations for the ClassFile API. While backports are a good way to adapt, as you see, backporting new features to old release tails has extra maintenance costs. That's why OpenJDK now promotes minimal critical fixes and no new fearures to tail release trains. The goal of ClassFile API is to allow users to read newer class files than the ones known at compile time. Hopefully by the next LTS 25, programs compiled with release 25 parsing class files can handle future classes easily so there will be no more gradle or asm upgrade hassles - users can actually run their programs smoothly on newer JDK versions. Chen On Sat, Dec 14, 2024, 6:32?AM David Lloyd wrote: > I found the problem, and it is not specific to the backport per se: there > are several places where `==` is used to compare `ClassDesc` > instances, particularly in the stack map generator. This usually works on > JDK 24 or later, but fails on 23 or earlier. The critical clue was when I > accidentally ran the backport version of the test on JDK 24 and it passed - > telling me that the problem was a JDK behavioral difference. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Fri Dec 20 20:33:50 2024 From: markro at cs.washington.edu (Mark Roberts) Date: Fri, 20 Dec 2024 12:33:50 -0800 Subject: MethodSignature.of(MethodTypeDescriptor) returns a descriptor not a signature? Message-ID: It looks wrong to me as it does not include any Type Arguments which is the primary difference between a Method Signature and a Method Descriptor, correct? Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From liangchenblue at gmail.com Fri Dec 20 21:25:59 2024 From: liangchenblue at gmail.com (Chen Liang) Date: Sat, 21 Dec 2024 05:25:59 +0800 Subject: MethodSignature.of(MethodTypeDescriptor) returns a descriptor not a signature? In-Reply-To: References: Message-ID: Hi Mark, this API and Signature.of(ClassDesc) both serve to create field/method signature objects from non-generic field or method types. They exist so operations supporting generic signatures can execute on non-generic types, which only have access to these two objects. Chen On Sat, Dec 21, 2024, 4:34?AM Mark Roberts wrote: > It looks wrong to me as it does not include any Type Arguments which is > the primary difference between a Method Signature and a Method Descriptor, > correct? > > > > Mark > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: