From markro at cs.washington.edu Wed Apr 2 19:12:21 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Wed, 2 Apr 2025 12:12:21 -0700 Subject: adding an interface to a class Message-ID: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> I might be missing something, but I see only a method to set all the interfaces of a class. So to add an interface I have to get the current list of interfaces, add mine to it and then reset the entire list. Is that correct? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Apr 2 19:25:28 2025 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 2 Apr 2025 15:25:28 -0400 Subject: adding an interface to a class In-Reply-To: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> References: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> Message-ID: <2e28689e-15c0-426d-a997-a7311263b6e7@oracle.com> Correct.? (For bonus points, you should also check to make sure it is not already in the list of implemented interfaces.) On 4/2/2025 3:12 PM, Mark Roberts wrote: > > I might be missing something, but I see only a method to set all the > interfaces of a class.? So to add an interface I have to get the > current list of interfaces, add mine to it and then reset the entire > list.? Is that correct? > > Thank you, > > Mark > -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Fri Apr 11 00:05:13 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Thu, 10 Apr 2025 17:05:13 -0700 Subject: adding an interface to a class In-Reply-To: <2e28689e-15c0-426d-a997-a7311263b6e7@oracle.com> References: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> <2e28689e-15c0-426d-a997-a7311263b6e7@oracle.com> Message-ID: <18e57402e3eefa106e35a829b2d2a78f@mail.gmail.com> I must be missing something. This works: ClassDesc cd = ClassDesc.of(name); // (name is string containing binary name) ClassEntry ce = poolBuilder.classEntry(cd); But this gets a compile error: Utf8Entry ue = poolBuilder.uft8Entry(name); // (name is string containing internal form) daikon/dcomp/ClassGen24.java:103: error: cannot find symbol Utf8Entry ue = poolBuilder.uft8Entry(name); ^ symbol: method uft8Entry(String) location: variable poolBuilder of type ConstantPoolBuilder ClassEntry ce = poolBuilder.classEntry(ue); Thank you, Mark *From:* Brian Goetz *Sent:* Wednesday, April 2, 2025 12:25 PM *To:* Mark Roberts ; Chen Liang < chen.l.liang at oracle.com>; classfile-api-dev at openjdk.org *Subject:* Re: adding an interface to a class Correct. (For bonus points, you should also check to make sure it is not already in the list of implemented interfaces.) On 4/2/2025 3:12 PM, Mark Roberts wrote: I might be missing something, but I see only a method to set all the interfaces of a class. So to add an interface I have to get the current list of interfaces, add mine to it and then reset the entire list. Is that correct? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From jarabekit at gmail.com Fri Apr 11 00:07:30 2025 From: jarabekit at gmail.com (Daniel Jarabek) Date: Thu, 10 Apr 2025 20:07:30 -0400 Subject: adding an interface to a class In-Reply-To: <18e57402e3eefa106e35a829b2d2a78f@mail.gmail.com> References: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> <2e28689e-15c0-426d-a997-a7311263b6e7@oracle.com> <18e57402e3eefa106e35a829b2d2a78f@mail.gmail.com> Message-ID: <5c07f6d8-2e1e-404f-b36c-284d2a7ba907@gmail.com> I think you mean utf8Entry instead of "uft8Entry". -DJ On 4/10/25 20:05, Mark Roberts wrote: > I must be missing something.? This works: > > ?? ???ClassDesc cd = ClassDesc.of(name); // (name is string containing > binary name) > > ?? ???ClassEntry ce = poolBuilder.classEntry(cd); > > But this gets a compile error: > > ??? ??Utf8Entry ue = poolBuilder.uft8Entry(name); // (name is string > containing internal form) > > daikon/dcomp/ClassGen24.java:103: error: cannot find symbol > > ??? Utf8Entry ue = poolBuilder.uft8Entry(name); > > ????????????????????????????? ?????????????????????^ > > ? symbol:?? method uft8Entry(String) > > ? location: variable poolBuilder of type ConstantPoolBuilder > > ?? ???ClassEntry ce = poolBuilder.classEntry(ue); > > Thank you, > > Mark > > *From:* Brian Goetz > > *Sent:* Wednesday, April 2, 2025 12:25 PM > *To:* Mark Roberts >; Chen Liang >; classfile-api-dev at openjdk.org > > *Subject:* Re: adding an interface to a class > > Correct.? (For bonus points, you should also check to make sure it is > not already in the list of implemented interfaces.) > > On 4/2/2025 3:12 PM, Mark Roberts wrote: > > I might be missing something, but I see only a method to set all the > interfaces of a class.? So to add an interface I have to get the > current list of interfaces, add mine to it and then reset the entire > list.? Is that correct? > > Thank you, > > Mark > From chen.l.liang at oracle.com Fri Apr 11 00:15:17 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Fri, 11 Apr 2025 00:15:17 +0000 Subject: [External] : RE: adding an interface to a class In-Reply-To: <18e57402e3eefa106e35a829b2d2a78f@mail.gmail.com> References: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> <2e28689e-15c0-426d-a997-a7311263b6e7@oracle.com> <18e57402e3eefa106e35a829b2d2a78f@mail.gmail.com> Message-ID: Might be that you have a class that's not java.lang.String but also has this simple name of String. Note that bere is not a technical support channel, and if you are reporting an issue, you should provide steps of reproduction or problem/code analysis. ________________________________ From: Mark Roberts Sent: Thursday, April 10, 2025 7:05 PM To: classfile-api-dev at openjdk.org Cc: Brian Goetz ; Chen Liang Subject: [External] : RE: adding an interface to a class I must be missing something. This works: ClassDesc cd = ClassDesc.of(name); // (name is string containing binary name) ClassEntry ce = poolBuilder.classEntry(cd); But this gets a compile error: Utf8Entry ue = poolBuilder.uft8Entry(name); // (name is string containing internal form) daikon/dcomp/ClassGen24.java:103: error: cannot find symbol Utf8Entry ue = poolBuilder.uft8Entry(name); ^ symbol: method uft8Entry(String) location: variable poolBuilder of type ConstantPoolBuilder ClassEntry ce = poolBuilder.classEntry(ue); Thank you, Mark From: Brian Goetz > Sent: Wednesday, April 2, 2025 12:25 PM To: Mark Roberts >; Chen Liang >; classfile-api-dev at openjdk.org Subject: Re: adding an interface to a class Correct. (For bonus points, you should also check to make sure it is not already in the list of implemented interfaces.) On 4/2/2025 3:12 PM, Mark Roberts wrote: I might be missing something, but I see only a method to set all the interfaces of a class. So to add an interface I have to get the current list of interfaces, add mine to it and then reset the entire list. Is that correct? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Fri Apr 11 00:24:04 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Thu, 10 Apr 2025 17:24:04 -0700 Subject: adding an interface to a class In-Reply-To: <5c07f6d8-2e1e-404f-b36c-284d2a7ba907@gmail.com> References: <26d5ffab14add717c4b946877ceefb07@mail.gmail.com> <2e28689e-15c0-426d-a997-a7311263b6e7@oracle.com> <18e57402e3eefa106e35a829b2d2a78f@mail.gmail.com> <5c07f6d8-2e1e-404f-b36c-284d2a7ba907@gmail.com> Message-ID: <63994ba187b8a8ff310ee076c718598e@mail.gmail.com> s..t - I stared at that for ages and missed the typo. Sorry - and thank you -----Original Message----- From: Daniel Jarabek Sent: Thursday, April 10, 2025 5:08 PM To: Mark Roberts ; classfile-api-dev at openjdk.org Subject: Re: adding an interface to a class I think you mean utf8Entry instead of "uft8Entry". -DJ On 4/10/25 20:05, Mark Roberts wrote: > I must be missing something. This works: > > ClassDesc cd = ClassDesc.of(name); // (name is string > containing binary name) > > ClassEntry ce = poolBuilder.classEntry(cd); > > But this gets a compile error: > > Utf8Entry ue = poolBuilder.uft8Entry(name); // (name is string > containing internal form) > > daikon/dcomp/ClassGen24.java:103: error: cannot find symbol > > Utf8Entry ue = poolBuilder.uft8Entry(name); > > ^ > > symbol: method uft8Entry(String) > > location: variable poolBuilder of type ConstantPoolBuilder > > ClassEntry ce = poolBuilder.classEntry(ue); > > Thank you, > > Mark > > *From:* Brian Goetz > > *Sent:* Wednesday, April 2, 2025 12:25 PM > *To:* Mark Roberts >; Chen Liang > >; > classfile-api-dev at openjdk.org > *Subject:* Re: adding an interface to a class > > Correct. (For bonus points, you should also check to make sure it is > not already in the list of implemented interfaces.) > > On 4/2/2025 3:12 PM, Mark Roberts wrote: > > I might be missing something, but I see only a method to set all the > interfaces of a class. So to add an interface I have to get the > current list of interfaces, add mine to it and then reset the entire > list. Is that correct? > > Thank you, > > Mark > From markro at cs.washington.edu Tue Apr 15 17:15:32 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Tue, 15 Apr 2025 10:15:32 -0700 Subject: modifying a newly added method Message-ID: If I have added a new method to my classfile with ClassBuilder.withMethod(...) I know it is not added to ClassModel.methods() as that is an immutable list from the original, unmodified classfile. Is there any way to get a MethodModel for this new method in order to modify it further? Thank you, Mark From chen.l.liang at oracle.com Tue Apr 15 17:32:33 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Tue, 15 Apr 2025 17:32:33 +0000 Subject: modifying a newly added method In-Reply-To: References: Message-ID: Hi Mark, Unfortunately there is no easy way to do this, but it is doable. One way can be that you start with a class transform with a dummy class model, and move your original class builder code into the atEnd of your first transform. Then, you can call andThen with the 2nd transform, which can get MethodModel views of methods built by your first transform. Pseudocode: Original: cf.build(name, clb -> /* oldLambdaCode */) Updated: cf.build(clb -> clb.transform(dummyClassModel, new ClassTransform() { @Override public void accept(ClassBuilder b, ClassEement e) {} // throw away @Override public void atEnd(ClassBuilder clb) { /* oldLambdaCode */ } }.andThen(/* your actual transform that can see your old lambda's methods as MethodModel*/)) That said, I had previously proposed to introduce something like CodeBuilder::transforming to other models, which could have probably covered your use case; however, other builders require extra arguments to be constructed (class name, field/method name and types) so we did not add these APIs back. If you have an idea how such additions can be done, feel free to share your ideas. Regards, Chen ________________________________ From: classfile-api-dev on behalf of Mark Roberts Sent: Tuesday, April 15, 2025 12:15 PM To: classfile-api-dev at openjdk.org Subject: modifying a newly added method If I have added a new method to my classfile with ClassBuilder.withMethod(...) I know it is not added to ClassModel.methods() as that is an immutable list from the original, unmodified classfile. Is there any way to get a MethodModel for this new method in order to modify it further? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Thu Apr 24 22:07:16 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Thu, 24 Apr 2025 15:07:16 -0700 Subject: modifying a newly added method In-Reply-To: References: Message-ID: <7e88c38c97602e3a2452fa7c4deaac0f@mail.gmail.com> This approach looks interesting. Just want to make sure I understand the control flow. The empty ?accept? is the first transform that does nothing. ?atEnd? is the second transform ?andThen is the third transform which will see any changes made by the second transform Correct? Thank you, Mark *From:* Chen Liang *Sent:* Tuesday, April 15, 2025 10:33 AM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Subject:* Re: modifying a newly added method Hi Mark, Unfortunately there is no easy way to do this, but it is doable. One way can be that you start with a class transform with a dummy class model, and move your original class builder code into the atEnd of your first transform. Then, you can call andThen with the 2nd transform, which can get MethodModel views of methods built by your first transform. Pseudocode: Original: cf.build(name, clb -> /* oldLambdaCode */) Updated: cf.build(clb -> clb.transform(dummyClassModel, new ClassTransform() { @Override public void accept(ClassBuilder b, ClassEement e) {} // throw away @Override public void atEnd(ClassBuilder clb) { /* oldLambdaCode */ } }.andThen(/* your actual transform that can see your old lambda's methods as MethodModel*/)) That said, I had previously proposed to introduce something like CodeBuilder::transforming to other models, which could have probably covered your use case; however, other builders require extra arguments to be constructed (class name, field/method name and types) so we did not add these APIs back. If you have an idea how such additions can be done, feel free to share your ideas. Regards, Chen ------------------------------ *From:* classfile-api-dev on behalf of Mark Roberts *Sent:* Tuesday, April 15, 2025 12:15 PM *To:* classfile-api-dev at openjdk.org *Subject:* modifying a newly added method If I have added a new method to my classfile with ClassBuilder.withMethod(...) I know it is not added to ClassModel.methods() as that is an immutable list from the original, unmodified classfile. Is there any way to get a MethodModel for this new method in order to modify it further? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Thu Apr 24 22:23:40 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Thu, 24 Apr 2025 22:23:40 +0000 Subject: [External] : RE: modifying a newly added method In-Reply-To: <7e88c38c97602e3a2452fa7c4deaac0f@mail.gmail.com> References: <7e88c38c97602e3a2452fa7c4deaac0f@mail.gmail.com> Message-ID: Hi Mark, No, accept and atEnd are both the first transform. No-op accept just drops elements of the transformed objects; atEnd is called when all accept calls are done, so here you are actually operating on a new class. I am thinking of adding transforming like that in CodeBuilder to other builders - CodeBuilder has this because CodeBuilder creation required no extra arguments. For other builders, I think we can just inherit arguments from the called builder. Chen ________________________________ From: Mark Roberts Sent: Thursday, April 24, 2025 5:07 PM To: Chen Liang ; classfile-api-dev at openjdk.org Subject: [External] : RE: modifying a newly added method This approach looks interesting. Just want to make sure I understand the control flow. The empty ?accept? is the first transform that does nothing. ?atEnd? is the second transform ?andThen is the third transform which will see any changes made by the second transform Correct? Thank you, Mark From: Chen Liang > Sent: Tuesday, April 15, 2025 10:33 AM To: Mark Roberts >; classfile-api-dev at openjdk.org Subject: Re: modifying a newly added method Hi Mark, Unfortunately there is no easy way to do this, but it is doable. One way can be that you start with a class transform with a dummy class model, and move your original class builder code into the atEnd of your first transform. Then, you can call andThen with the 2nd transform, which can get MethodModel views of methods built by your first transform. Pseudocode: Original: cf.build(name, clb -> /* oldLambdaCode */) Updated: cf.build(clb -> clb.transform(dummyClassModel, new ClassTransform() { @Override public void accept(ClassBuilder b, ClassEement e) {} // throw away @Override public void atEnd(ClassBuilder clb) { /* oldLambdaCode */ } }.andThen(/* your actual transform that can see your old lambda's methods as MethodModel*/)) That said, I had previously proposed to introduce something like CodeBuilder::transforming to other models, which could have probably covered your use case; however, other builders require extra arguments to be constructed (class name, field/method name and types) so we did not add these APIs back. If you have an idea how such additions can be done, feel free to share your ideas. Regards, Chen ________________________________ From: classfile-api-dev > on behalf of Mark Roberts > Sent: Tuesday, April 15, 2025 12:15 PM To: classfile-api-dev at openjdk.org > Subject: modifying a newly added method If I have added a new method to my classfile with ClassBuilder.withMethod(...) I know it is not added to ClassModel.methods() as that is an immutable list from the original, unmodified classfile. Is there any way to get a MethodModel for this new method in order to modify it further? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Fri Apr 25 21:54:05 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Fri, 25 Apr 2025 14:54:05 -0700 Subject: MethodTypeDesc.parameterList() Message-ID: There seems to be an inconsistency in the treatment of parameters to inner class constructors. Given that these two classes are inner classes: private class Entry { final double key; final Object value; public Entry(double key, Object value) { this.key = key; this.value = value; } } public class DuplicateElementException extends RuntimeException { DuplicateElementException() { } DuplicateElementException(String s) { super(s); } } For the first, the local vars: this, key, value Param types: MapQuick.PriorityQueue, double, java.lang.Object Note that the type of ?this? is included in the list ? this is not true for every other case I have seen. For the second, local vars: this, this$0 Param types: MapQuick.PriorityQueue This agrees with the ?normal? case as MapQuick.PriorityQueue is associated with ?this$0? and the type for ?this? is not included. I had to add a lot of ugly code to work around this. It seems like including the type of the ?this? pointer is a problem or am I missing something? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Fri Apr 25 22:02:16 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Fri, 25 Apr 2025 22:02:16 +0000 Subject: [External] : MethodTypeDesc.parameterList() In-Reply-To: References: Message-ID: Excuse me, but I don't understand anything you are talking about here. Note this channel is for discussion of the Class-File API instead of support questions. Both classes are inner classes, which are nested classes that are not static. (Refer to JLS section 8.1.3). All constructors of inner classes have a mandated (implicit, but required to be present by specification) argument that passes the immediately enclosing instance. See JLS section 15.9.2 for determining enclosing instances. The JLS is available at https://docs.oracle.com/javase/specs. You can find the sections in the menu. In short, both cases will have enclosing instances and an implicit outer-this argument in the constructors because their declarations are not static classes. That is, unless you are declaring as member of interfaces; interface member classes are implicitly public and static, which does not seem to be your case as you are declaring the first class as private. If you have more questions, please go to compiler-dev list. Regards, Chen Liang ________________________________ From: Mark Roberts Sent: Friday, April 25, 2025 4:54 PM To: classfile-api-dev at openjdk.org Cc: Chen Liang Subject: [External] : MethodTypeDesc.parameterList() There seems to be an inconsistency in the treatment of parameters to inner class constructors. Given that these two classes are inner classes: private class Entry { final double key; final Object value; public Entry(double key, Object value) { this.key = key; this.value = value; } } public class DuplicateElementException extends RuntimeException { DuplicateElementException() { } DuplicateElementException(String s) { super(s); } } For the first, the local vars: this, key, value Param types: MapQuick.PriorityQueue, double, java.lang.Object Note that the type of ?this? is included in the list ? this is not true for every other case I have seen. For the second, local vars: this, this$0 Param types: MapQuick.PriorityQueue This agrees with the ?normal? case as MapQuick.PriorityQueue is associated with ?this$0? and the type for ?this? is not included. I had to add a lot of ugly code to work around this. It seems like including the type of the ?this? pointer is a problem or am I missing something? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Mon Apr 28 02:25:02 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Mon, 28 Apr 2025 02:25:02 +0000 Subject: Heads up: Recent news in the ClassFile API since Java 24 Message-ID: Hello all, Since the release of Java 24, I wish to share some latest news about the upcoming features in the ClassFile API. 1. UnknownAttribute and CustomAttribute are now delivered in traversal of CodeModel. https://bugs.openjdk.org/browse/JDK-8347472 2. StackMapsTableAttribute reused for NO_STACK_MAPS transformation (such as System.out.println on method entry) now updates its BCIs. (Same above, JDK-8347472) 3. The default flags of ClassBuilder is now ACC_PUBLIC + ACC_SUPER, in anticipation of identity classes when Value Objects JEP is integrated. And there are a few proposed features, please don't hesitate to comment or review: 1. Add matches() methods to CP entries for convenient and fast checking. https://github.com/openjdk/jdk/pull/23548 2. Add explicit counter support for performance-sensitive code building. https://github.com/openjdk/jdk/pull/24903 3. Add XxxBuilder::transforming the same way CodeBuilder::transforming behaves. https://github.com/openjdk/jdk/pull/24908 Note for XxxBuilder::transforming: It covers this ASM usage pattern currently not supported by the ClassFile API: // ASM ClassVisitor cv = new DelegateClassVisitor(new ClassWriter(...)); cv.visitXxx(); // write elements through the delegate // ClassFile API cf.build(..., clb0 -> clb0.transforming((clb, cle) -> /*process */, clb -> { // write elements through delegate })); And it introduces a source incompatibility if you are using ClassFileBuilder as a variable type, like ClassFileBuilder. See this draft release note https://bugs.openjdk.org/browse/JDK-8355665 for migration help. Also on the Valhalla side: ClassFile API is providing first-class support for strict fields and the updated stack map table format, including parsing, manual creation, and automatic generation. Please feel free to review the PRs linked! And don't hesitate to reply with any comment or question. Regards, Chen Liang -------------- next part -------------- An HTML attachment was scrubbed... URL: