From markro at cs.washington.edu Sun Jan 5 20:57:33 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Sun, 5 Jan 2025 12:57:33 -0800 Subject: ClassFile.build() is loading result file Message-ID: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> I am using ClassFileTransformer.transform() to instrument class files as they are loaded. Within my transform method I call byte[] newBytes = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> modifyClass(classBuilder, classModel, loader)); where modifyClass does all the instrumentation. The problem is some where inside of classFile.build() the class is actually being loaded! Thus, when I return newBytes from the transform method I get an error ?attempted duplicate abstract class definition?. I have verified that the class has not be loaded prior to the return from modifyClass(). What is going on? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From liangchenblue at gmail.com Sun Jan 5 22:06:03 2025 From: liangchenblue at gmail.com (Chen Liang) Date: Sun, 5 Jan 2025 16:06:03 -0600 Subject: ClassFile.build() is loading result file In-Reply-To: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> References: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> Message-ID: Hi Mark, why do you need to supply the ClassLoader to the modifyClass method? Class file transformation should have no dependency on the CHA process, and the only need is encapsulated within the ClassFile context object. Can you post your modifyClass? Chen On Sun, Jan 5, 2025, 2:57?PM Mark Roberts wrote: > I am using ClassFileTransformer.transform() to instrument class files as > they are loaded. Within my transform method I call > > > > byte[] newBytes = classFile.build(classModel.thisClass().asSymbol(), > > classBuilder -> modifyClass(classBuilder, > classModel, loader)); > > > > where modifyClass does all the instrumentation. > > > > The problem is some where inside of classFile.build() the class is > actually being loaded! Thus, when I return newBytes from the transform > method I get an error ?attempted duplicate abstract class definition?. I > have verified that the class has not be loaded prior to the return from > modifyClass(). > > > > What is going on? > > > > Thank you, > > Mark > -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Wed Jan 8 02:13:03 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Tue, 7 Jan 2025 18:13:03 -0800 Subject: ClassFile.build() is loading result file In-Reply-To: 88e022ef57a2bd268d0f4ee984f19415@mail.gmail.com References: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> 88e022ef57a2bd268d0f4ee984f19415@mail.gmail.com Message-ID: <16c10530682b850e47217497b2394bf4@mail.gmail.com> OK ? I build my own version of java.base so I could debug this issue. It looks like the problem is that when StackMapGenerator wants to see if something is an interface, the code ends up doing a Class.forName and loads the class being built prematurely. Here is the interesting call stack: at java.base/jdk.internal.loader.URLClassPath$FileLoader.getResource(URLClassPath.java:1073) at java.base/jdk.internal.loader.URLClassPath.getResource(URLClassPath.java:315) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:757) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:582) at java.base/java.lang.Class.forName(Class.java:561) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:223) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:219) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:243) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:134) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:131) at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1229) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:142) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:74) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:86) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1547) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeFrom(StackMapGenerator.java:1515) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.merge(StackMapGenerator.java:1376) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.checkAssignableTo(StackMapGenerator.java:1326) at java.base/jdk.internal.classfile.impl.StackMapGenerator.processMethod(StackMapGenerator.java:433) at java.base/jdk.internal.classfile.impl.StackMapGenerator.generate(StackMapGenerator.java:302) at java.base/jdk.internal.classfile.impl.StackMapGenerator.(StackMapGenerator.java:245) at java.base/jdk.internal.classfile.impl.StackMapGenerator.of(StackMapGenerator.java:156) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.generateStackMaps(DirectCodeBuilder.java:317) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.tryGenerateStackMaps(DirectCodeBuilder.java:325) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.writeBody(DirectCodeBuilder.java:360) at java.base/jdk.internal.classfile.impl.UnboundAttribute$AdHocAttribute.writeTo(UnboundAttribute.java:835) at java.base/jdk.internal.classfile.impl.Util.writeAttribute(Util.java:230) at java.base/jdk.internal.classfile.impl.AttributeHolder.writeTo(AttributeHolder.java:71) at java.base/jdk.internal.classfile.impl.DirectMethodBuilder.writeTo(DirectMethodBuilder.java:146) at java.base/jdk.internal.classfile.impl.Util.writeList(Util.java:247) at java.base/jdk.internal.classfile.impl.DirectClassBuilder.build(DirectClassBuilder.java:198) at java.base/jdk.internal.classfile.impl.ClassFileImpl.build(ClassFileImpl.java:146) at java.base/java.lang.classfile.ClassFile.build(ClassFile.java:333) at daikon.chicory.Instrument.transform(Instrument.java:295) at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:259) at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188) at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:611) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at javautil.ArrayList17Test.main(ArrayList17Test.java:7) Thank you, Mark *From:* Mark Roberts *Sent:* Monday, January 6, 2025 12:51 PM *To:* 'Chen Liang' *Cc:* 'classfile-api-dev' *Subject:* RE: ClassFile.build() is loading result file I modified the jvmti minst tool to capture java execution method enter and exit. I have attached what I hope is the relevant part of the log file. This section of the log file starts with: daikon.chicory.Instrument.modifyClass exit and ends with: java.lang.LinkageError: loader 'app' attempted duplicate abstract class definition for javautil.AbstractList17. Along the way you can see the load: [6.689s][info][class,load] javautil.AbstractList17 source: file:/scratch/markro/tests/scratch/markro/clones/mydaikon.24/tests/daikon-tests/ArrayList17/ Happening twice. The second time is expected when the code exits the transform method. This first seems like an error that occurs at some point while the DirectCodeBuilder is running. Hope this helps to debug my problem. Thank you, Mark *From:* Chen Liang *Sent:* Sunday, January 5, 2025 2:06 PM *To:* Mark Roberts *Cc:* classfile-api-dev *Subject:* Re: ClassFile.build() is loading result file Hi Mark, why do you need to supply the ClassLoader to the modifyClass method? Class file transformation should have no dependency on the CHA process, and the only need is encapsulated within the ClassFile context object. Can you post your modifyClass? Chen On Sun, Jan 5, 2025, 2:57?PM Mark Roberts wrote: I am using ClassFileTransformer.transform() to instrument class files as they are loaded. Within my transform method I call byte[] newBytes = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> modifyClass(classBuilder, classModel, loader)); where modifyClass does all the instrumentation. The problem is some where inside of classFile.build() the class is actually being loaded! Thus, when I return newBytes from the transform method I get an error ?attempted duplicate abstract class definition?. I have verified that the class has not be loaded prior to the return from modifyClass(). What is going on? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jan 8 03:14:23 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 8 Jan 2025 03:14:23 +0000 Subject: ClassFile.build() is loading result file In-Reply-To: <16c10530682b850e47217497b2394bf4@mail.gmail.com> References: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> 88e022ef57a2bd268d0f4ee984f19415@mail.gmail.com <16c10530682b850e47217497b2394bf4@mail.gmail.com> Message-ID: Thanks for this investigation. I should definitely add a note to tge class hierarchy resolver option about this side effect when this is used by instrumentation. Chen Get Outlook for Android ________________________________ From: classfile-api-dev on behalf of Mark Roberts Sent: Tuesday, January 7, 2025 8:13:03 PM To: Chen Liang Cc: classfile-api-dev Subject: RE: ClassFile.build() is loading result file OK ? I build my own version of java.base so I could debug this issue. It looks like the problem is that when StackMapGenerator wants to see if something is an interface, the code ends up doing a Class.forName and loads the class being built prematurely. Here is the interesting call stack: at java.base/jdk.internal.loader.URLClassPath$FileLoader.getResource(URLClassPath.java:1073) at java.base/jdk.internal.loader.URLClassPath.getResource(URLClassPath.java:315) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:757) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:582) at java.base/java.lang.Class.forName(Class.java:561) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:223) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:219) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:243) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:134) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:131) at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1229) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:142) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:74) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:86) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1547) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeFrom(StackMapGenerator.java:1515) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.merge(StackMapGenerator.java:1376) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.checkAssignableTo(StackMapGenerator.java:1326) at java.base/jdk.internal.classfile.impl.StackMapGenerator.processMethod(StackMapGenerator.java:433) at java.base/jdk.internal.classfile.impl.StackMapGenerator.generate(StackMapGenerator.java:302) at java.base/jdk.internal.classfile.impl.StackMapGenerator.(StackMapGenerator.java:245) at java.base/jdk.internal.classfile.impl.StackMapGenerator.of(StackMapGenerator.java:156) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.generateStackMaps(DirectCodeBuilder.java:317) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.tryGenerateStackMaps(DirectCodeBuilder.java:325) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.writeBody(DirectCodeBuilder.java:360) at java.base/jdk.internal.classfile.impl.UnboundAttribute$AdHocAttribute.writeTo(UnboundAttribute.java:835) at java.base/jdk.internal.classfile.impl.Util.writeAttribute(Util.java:230) at java.base/jdk.internal.classfile.impl.AttributeHolder.writeTo(AttributeHolder.java:71) at java.base/jdk.internal.classfile.impl.DirectMethodBuilder.writeTo(DirectMethodBuilder.java:146) at java.base/jdk.internal.classfile.impl.Util.writeList(Util.java:247) at java.base/jdk.internal.classfile.impl.DirectClassBuilder.build(DirectClassBuilder.java:198) at java.base/jdk.internal.classfile.impl.ClassFileImpl.build(ClassFileImpl.java:146) at java.base/java.lang.classfile.ClassFile.build(ClassFile.java:333) at daikon.chicory.Instrument.transform(Instrument.java:295) at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:259) at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188) at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:611) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at javautil.ArrayList17Test.main(ArrayList17Test.java:7) Thank you, Mark From: Mark Roberts > Sent: Monday, January 6, 2025 12:51 PM To: 'Chen Liang' > Cc: 'classfile-api-dev' > Subject: RE: ClassFile.build() is loading result file I modified the jvmti minst tool to capture java execution method enter and exit. I have attached what I hope is the relevant part of the log file. This section of the log file starts with: daikon.chicory.Instrument.modifyClass exit and ends with: java.lang.LinkageError: loader 'app' attempted duplicate abstract class definition for javautil.AbstractList17. Along the way you can see the load: [6.689s][info][class,load] javautil.AbstractList17 source: file:/scratch/markro/tests/scratch/markro/clones/mydaikon.24/tests/daikon-tests/ArrayList17/ Happening twice. The second time is expected when the code exits the transform method. This first seems like an error that occurs at some point while the DirectCodeBuilder is running. Hope this helps to debug my problem. Thank you, Mark From: Chen Liang > Sent: Sunday, January 5, 2025 2:06 PM To: Mark Roberts > Cc: classfile-api-dev > Subject: Re: ClassFile.build() is loading result file Hi Mark, why do you need to supply the ClassLoader to the modifyClass method? Class file transformation should have no dependency on the CHA process, and the only need is encapsulated within the ClassFile context object. Can you post your modifyClass? Chen On Sun, Jan 5, 2025, 2:57?PM Mark Roberts > wrote: I am using ClassFileTransformer.transform() to instrument class files as they are loaded. Within my transform method I call byte[] newBytes = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> modifyClass(classBuilder, classModel, loader)); where modifyClass does all the instrumentation. The problem is some where inside of classFile.build() the class is actually being loaded! Thus, when I return newBytes from the transform method I get an error ?attempted duplicate abstract class definition?. I have verified that the class has not be loaded prior to the return from modifyClass(). What is going on? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jan 8 14:32:13 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 8 Jan 2025 14:32:13 +0000 Subject: [External] : RE: ClassFile.build() is loading result file In-Reply-To: References: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> 88e022ef57a2bd268d0f4ee984f19415@mail.gmail.com <16c10530682b850e47217497b2394bf4@mail.gmail.com> Message-ID: Use ClassHierarchyResolver.ofResourceParsing instead. Get Outlook for Android ________________________________ From: Mark Roberts Sent: Wednesday, January 8, 2025 8:02:16 AM To: Chen Liang Subject: [External] : RE: ClassFile.build() is loading result file I?m sorry but I don?t understand. A documentation note doesn?t solve my problem. What do I have to do to keep this from happening? Mark From: Chen Liang > Sent: Tuesday, January 7, 2025 7:14 PM To: Mark Roberts >; Chen Liang > Cc: classfile-api-dev > Subject: Re: ClassFile.build() is loading result file Thanks for this investigation. I should definitely add a note to tge class hierarchy resolver option about this side effect when this is used by instrumentation. Chen Get Outlook for Android ________________________________ From: classfile-api-dev > on behalf of Mark Roberts > Sent: Tuesday, January 7, 2025 8:13:03 PM To: Chen Liang > Cc: classfile-api-dev > Subject: RE: ClassFile.build() is loading result file OK ? I build my own version of java.base so I could debug this issue. It looks like the problem is that when StackMapGenerator wants to see if something is an interface, the code ends up doing a Class.forName and loads the class being built prematurely. Here is the interesting call stack: at java.base/jdk.internal.loader.URLClassPath$FileLoader.getResource(URLClassPath.java:1073) at java.base/jdk.internal.loader.URLClassPath.getResource(URLClassPath.java:315) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:757) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:582) at java.base/java.lang.Class.forName(Class.java:561) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:223) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:219) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:243) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:134) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:131) at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1229) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:142) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:74) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:86) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1547) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeFrom(StackMapGenerator.java:1515) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.merge(StackMapGenerator.java:1376) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.checkAssignableTo(StackMapGenerator.java:1326) at java.base/jdk.internal.classfile.impl.StackMapGenerator.processMethod(StackMapGenerator.java:433) at java.base/jdk.internal.classfile.impl.StackMapGenerator.generate(StackMapGenerator.java:302) at java.base/jdk.internal.classfile.impl.StackMapGenerator.(StackMapGenerator.java:245) at java.base/jdk.internal.classfile.impl.StackMapGenerator.of(StackMapGenerator.java:156) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.generateStackMaps(DirectCodeBuilder.java:317) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.tryGenerateStackMaps(DirectCodeBuilder.java:325) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.writeBody(DirectCodeBuilder.java:360) at java.base/jdk.internal.classfile.impl.UnboundAttribute$AdHocAttribute.writeTo(UnboundAttribute.java:835) at java.base/jdk.internal.classfile.impl.Util.writeAttribute(Util.java:230) at java.base/jdk.internal.classfile.impl.AttributeHolder.writeTo(AttributeHolder.java:71) at java.base/jdk.internal.classfile.impl.DirectMethodBuilder.writeTo(DirectMethodBuilder.java:146) at java.base/jdk.internal.classfile.impl.Util.writeList(Util.java:247) at java.base/jdk.internal.classfile.impl.DirectClassBuilder.build(DirectClassBuilder.java:198) at java.base/jdk.internal.classfile.impl.ClassFileImpl.build(ClassFileImpl.java:146) at java.base/java.lang.classfile.ClassFile.build(ClassFile.java:333) at daikon.chicory.Instrument.transform(Instrument.java:295) at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:259) at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188) at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:611) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at javautil.ArrayList17Test.main(ArrayList17Test.java:7) Thank you, Mark From: Mark Roberts > Sent: Monday, January 6, 2025 12:51 PM To: 'Chen Liang' > Cc: 'classfile-api-dev' > Subject: RE: ClassFile.build() is loading result file I modified the jvmti minst tool to capture java execution method enter and exit. I have attached what I hope is the relevant part of the log file. This section of the log file starts with: daikon.chicory.Instrument.modifyClass exit and ends with: java.lang.LinkageError: loader 'app' attempted duplicate abstract class definition for javautil.AbstractList17. Along the way you can see the load: [6.689s][info][class,load] javautil.AbstractList17 source: file:/scratch/markro/tests/scratch/markro/clones/mydaikon.24/tests/daikon-tests/ArrayList17/ Happening twice. The second time is expected when the code exits the transform method. This first seems like an error that occurs at some point while the DirectCodeBuilder is running. Hope this helps to debug my problem. Thank you, Mark From: Chen Liang > Sent: Sunday, January 5, 2025 2:06 PM To: Mark Roberts > Cc: classfile-api-dev > Subject: Re: ClassFile.build() is loading result file Hi Mark, why do you need to supply the ClassLoader to the modifyClass method? Class file transformation should have no dependency on the CHA process, and the only need is encapsulated within the ClassFile context object. Can you post your modifyClass? Chen On Sun, Jan 5, 2025, 2:57?PM Mark Roberts > wrote: I am using ClassFileTransformer.transform() to instrument class files as they are loaded. Within my transform method I call byte[] newBytes = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> modifyClass(classBuilder, classModel, loader)); where modifyClass does all the instrumentation. The problem is some where inside of classFile.build() the class is actually being loaded! Thus, when I return newBytes from the transform method I get an error ?attempted duplicate abstract class definition?. I have verified that the class has not be loaded prior to the return from modifyClass(). What is going on? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jan 8 16:36:05 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 8 Jan 2025 16:36:05 +0000 Subject: [External] : RE: ClassFile.build() is loading result file In-Reply-To: References: <7534bb8491cf5e1cbd4213992b8bf36d@mail.gmail.com> 88e022ef57a2bd268d0f4ee984f19415@mail.gmail.com <16c10530682b850e47217497b2394bf4@mail.gmail.com> <5dd4b6c925a14d5ad61a67578db55f5f@mail.gmail.com> Message-ID: So ClassHierarchyResolver.ofClassLoading loads system classes and inspect them with core reflection to determine their class hierarchy, based on the assumptions that the system classes are constant. However, such assumptions do not stand when agents are present, and agents do not wish system class loaded in the process of handling class files. As a result, we should inspect the class file resources instead, which is largely the same as agent-transformed classes (as agents don't usually change super classes or change classes to interfaces) P.S. Forwarding this conversation to classfile-api-dev as this may be valuable to the public. Regards, Chen ________________________________ From: Mark Roberts Sent: Wednesday, January 8, 2025 10:15 AM To: Chen Liang Subject: RE: [External] : RE: ClassFile.build() is loading result file Ok that worked ? thank you. Could you give me a little background on what?s going on and why the option is needed? Thanks, Mark From: Chen Liang > Sent: Wednesday, January 8, 2025 8:05 AM To: Mark Roberts > Subject: Re: [External] : RE: ClassFile.build() is loading result file Yep; for the loader, if you are just transforming system classes, ClassLoader.getSystemClassLoader() should be sufficient. ________________________________ From: Mark Roberts > Sent: Wednesday, January 8, 2025 9:28 AM To: Chen Liang > Subject: RE: [External] : RE: ClassFile.build() is loading result file So would that be: ClassFile classFile = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofResourceParsing(loader))); From: Chen Liang > Sent: Wednesday, January 8, 2025 6:32 AM To: Mark Roberts >; classfile-api-dev at openjdk.org Subject: Re: [External] : RE: ClassFile.build() is loading result file Use ClassHierarchyResolver.ofResourceParsing instead. Get Outlook for Android ________________________________ From: Mark Roberts > Sent: Wednesday, January 8, 2025 8:02:16 AM To: Chen Liang > Subject: [External] : RE: ClassFile.build() is loading result file I?m sorry but I don?t understand. A documentation note doesn?t solve my problem. What do I have to do to keep this from happening? Mark From: Chen Liang > Sent: Tuesday, January 7, 2025 7:14 PM To: Mark Roberts >; Chen Liang > Cc: classfile-api-dev > Subject: Re: ClassFile.build() is loading result file Thanks for this investigation. I should definitely add a note to tge class hierarchy resolver option about this side effect when this is used by instrumentation. Chen Get Outlook for Android ________________________________ From: classfile-api-dev > on behalf of Mark Roberts > Sent: Tuesday, January 7, 2025 8:13:03 PM To: Chen Liang > Cc: classfile-api-dev > Subject: RE: ClassFile.build() is loading result file OK ? I build my own version of java.base so I could debug this issue. It looks like the problem is that when StackMapGenerator wants to see if something is an interface, the code ends up doing a Class.forName and loads the class being built prematurely. Here is the interesting call stack: at java.base/jdk.internal.loader.URLClassPath$FileLoader.getResource(URLClassPath.java:1073) at java.base/jdk.internal.loader.URLClassPath.getResource(URLClassPath.java:315) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:757) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:582) at java.base/java.lang.Class.forName(Class.java:561) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:223) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:219) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$ClassLoadingClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:243) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:134) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver$1.apply(ClassHierarchyImpl.java:131) at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1229) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl$CachedClassHierarchyResolver.getClassInfo(ClassHierarchyImpl.java:142) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:74) at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:86) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1547) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeFrom(StackMapGenerator.java:1515) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.merge(StackMapGenerator.java:1376) at java.base/jdk.internal.classfile.impl.StackMapGenerator$Frame.checkAssignableTo(StackMapGenerator.java:1326) at java.base/jdk.internal.classfile.impl.StackMapGenerator.processMethod(StackMapGenerator.java:433) at java.base/jdk.internal.classfile.impl.StackMapGenerator.generate(StackMapGenerator.java:302) at java.base/jdk.internal.classfile.impl.StackMapGenerator.(StackMapGenerator.java:245) at java.base/jdk.internal.classfile.impl.StackMapGenerator.of(StackMapGenerator.java:156) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.generateStackMaps(DirectCodeBuilder.java:317) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.tryGenerateStackMaps(DirectCodeBuilder.java:325) at java.base/jdk.internal.classfile.impl.DirectCodeBuilder$4.writeBody(DirectCodeBuilder.java:360) at java.base/jdk.internal.classfile.impl.UnboundAttribute$AdHocAttribute.writeTo(UnboundAttribute.java:835) at java.base/jdk.internal.classfile.impl.Util.writeAttribute(Util.java:230) at java.base/jdk.internal.classfile.impl.AttributeHolder.writeTo(AttributeHolder.java:71) at java.base/jdk.internal.classfile.impl.DirectMethodBuilder.writeTo(DirectMethodBuilder.java:146) at java.base/jdk.internal.classfile.impl.Util.writeList(Util.java:247) at java.base/jdk.internal.classfile.impl.DirectClassBuilder.build(DirectClassBuilder.java:198) at java.base/jdk.internal.classfile.impl.ClassFileImpl.build(ClassFileImpl.java:146) at java.base/java.lang.classfile.ClassFile.build(ClassFile.java:333) at daikon.chicory.Instrument.transform(Instrument.java:295) at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:259) at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188) at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:611) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1026) at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:528) at javautil.ArrayList17Test.main(ArrayList17Test.java:7) Thank you, Mark From: Mark Roberts > Sent: Monday, January 6, 2025 12:51 PM To: 'Chen Liang' > Cc: 'classfile-api-dev' > Subject: RE: ClassFile.build() is loading result file I modified the jvmti minst tool to capture java execution method enter and exit. I have attached what I hope is the relevant part of the log file. This section of the log file starts with: daikon.chicory.Instrument.modifyClass exit and ends with: java.lang.LinkageError: loader 'app' attempted duplicate abstract class definition for javautil.AbstractList17. Along the way you can see the load: [6.689s][info][class,load] javautil.AbstractList17 source: file:/scratch/markro/tests/scratch/markro/clones/mydaikon.24/tests/daikon-tests/ArrayList17/ Happening twice. The second time is expected when the code exits the transform method. This first seems like an error that occurs at some point while the DirectCodeBuilder is running. Hope this helps to debug my problem. Thank you, Mark From: Chen Liang > Sent: Sunday, January 5, 2025 2:06 PM To: Mark Roberts > Cc: classfile-api-dev > Subject: Re: ClassFile.build() is loading result file Hi Mark, why do you need to supply the ClassLoader to the modifyClass method? Class file transformation should have no dependency on the CHA process, and the only need is encapsulated within the ClassFile context object. Can you post your modifyClass? Chen On Sun, Jan 5, 2025, 2:57?PM Mark Roberts > wrote: I am using ClassFileTransformer.transform() to instrument class files as they are loaded. Within my transform method I call byte[] newBytes = classFile.build(classModel.thisClass().asSymbol(), classBuilder -> modifyClass(classBuilder, classModel, loader)); where modifyClass does all the instrumentation. The problem is some where inside of classFile.build() the class is actually being loaded! Thus, when I return newBytes from the transform method I get an error ?attempted duplicate abstract class definition?. I have verified that the class has not be loaded prior to the return from modifyClass(). What is going on? Thank you, Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Tue Jan 28 20:45:32 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Tue, 28 Jan 2025 12:45:32 -0800 Subject: StackMap problem Message-ID: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- public static void push() { try { s.push(createItem(rnd.nextInt(4))); } catch (Overflow e) { } observe(); } -------------- next part -------------- name_index: 46 (push) access_flags: Const.ACC_PUBLIC | Const.ACC_STATIC descriptor_index: 6 (()V) attribute count: 1 attribute_name_index: 85 (Code) Code(maxStack = 3, maxLocals = 1, code_length = 24) 0: getstatic DataStructures.StackArTester.s LDataStructures/StackAr; (12) 3: getstatic DataStructures.StackArTester.rnd Ljava/util/Random; (30) 6: iconst_4 7: invokevirtual java.util.Random.nextInt (I)I (34) 10: invokestatic DataStructures.StackArTester.createItem (I)Ljava/lang/Object; (40) 13: invokevirtual DataStructures.StackAr.push (Ljava/lang/Object;)V (44) 16: goto #20 19: astore_0 20: invokestatic DataStructures.StackArTester.observe ()V (21) 23: return Exception handler(s) = From To Handler Type 0 16 19 DataStructures.Overflow(48) Attribute(s) = LineNumberTable: LineNumber(0, 37), LineNumber(16, 38), LineNumber(20, 39), LineNumber(23, 40) LocalVariableTable: StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=19, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) -------------- next part -------------- name_index: 64 (push) access_flags: Const.ACC_PUBLIC | Const.ACC_STATIC descriptor_index: 4 (()V) attribute count: 1 attribute_name_index: 129 (Code) Code(maxStack = 6, maxLocals = 2, code_length = 54) 0: getstatic daikon.chicory.Runtime.nonce Ljava/util/concurrent/atomic/AtomicInteger; (10) 3: invokevirtual java.util.concurrent.atomic.AtomicInteger.getAndIncrement ()I (16) 6: istore_1 7: aconst_null 8: iload_1 9: iconst_3 10: iconst_0 11: anewarray (18) 14: invokestatic daikon.chicory.Runtime.enter (Ljava/lang/Object;II[Ljava/lang/Object;)V (22) 17: getstatic DataStructures.StackArTester.s LDataStructures/StackAr; (46) 20: getstatic DataStructures.StackArTester.rnd Ljava/util/Random; (70) 23: iconst_4 24: invokevirtual java.util.Random.nextInt (I)I (76) 27: invokestatic DataStructures.StackArTester.createItem (I)Ljava/lang/Object; (78) 30: invokevirtual DataStructures.StackAr.push (Ljava/lang/Object;)V (81) 33: goto #37 36: astore_0 37: invokestatic DataStructures.StackArTester.observe ()V (52) 40: aconst_null 41: iload_1 42: iconst_3 43: iconst_0 44: anewarray (18) 47: aconst_null 48: bipush 40 50: invokestatic daikon.chicory.Runtime.exit (Ljava/lang/Object;II[Ljava/lang/Object;Ljava/lang/Object;I)V (26) 53: return Exception handler(s) = From To Handler Type 0 33 36 DataStructures.Overflow(66) Attribute(s) = LocalVariableTable: LocalVariable(startPc = 0, length = 54, index = 1:int this_invocation_nonce) LineNumberTable: LineNumber(17, 37), LineNumber(33, 38), LineNumber(37, 39), LineNumber(40, 40) StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=36, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) From chen.l.liang at oracle.com Tue Jan 28 22:46:02 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Tue, 28 Jan 2025 22:46:02 +0000 Subject: [External] : StackMap problem In-Reply-To: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> Message-ID: Hi Mark, This indeed is weird - the ClassFile API's generation should force another branching check at bci 6 against the handler at 36 for frame compatibility, and should fail there. Unfortunately, I am busy with other projects right now and cannot divest a lot of time investigating this; can you try creating a minimal reproduction, like a simple method that has a try-catch and a post action, and a very simple transform, both of which don't require extra library dependencies? Regards, Chen ________________________________ From: Mark Roberts Sent: Tuesday, January 28, 2025 2:45 PM To: classfile-api-dev at openjdk.org Cc: Chen Liang Subject: [External] : StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Wed Jan 29 00:39:51 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Tue, 28 Jan 2025 16:39:51 -0800 Subject: labels Message-ID: I am using the technique we discussed to do my instrumentation: - Get a copy of the code.elementList() - Insert new instructions into this list, and/or modify existing ones - Output the modified instruction list using the codeBuilder.with() method The problem I am having is I don?t see how to create new labels since I am not allowed to insert a LabelTarget into my modified code list. My vague idea is that I have to keep track of their locations in a separate list and then call codeBuilder.labelBinding(my new label) at the appropriate place in between calls to codeBuilder.with()? That seems weird ? there must be a better way? Thank you, Mark *From:* Chen Liang *Sent:* Wednesday, November 27, 2024 4:01 PM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Subject:* Re: [External] : more class file transformation questions Hi Mark, 1. No, you have to do new ArrayList<>(code.elementList()) as the list returned is immutable. 2. To insert an instruction, just use List::add. To get an instruction, create one from the factory methods (usually named "of") of Instruction subclasses. Once you are done with the list, do for (var e : list) codeBuilder.with(e); 3. Bound and unbound instructions have no difference in usage when you write them. The bound instruction can be seen as a specialized version of unbound instruction, that they are cheaper for the API to obtain and supports fast copying to another CodeBuilder when condition allows (that is, the new CodeBuilder has a constantpool that extends from the constant pool of the bound instruction). Bound instructions can only be read from Class Files; unbound instructions can always be created by factory methods on the instruction interfaces. Regards, Chen ------------------------------ *From:* Mark Roberts *Sent:* Wednesday, November 27, 2024 5:54 PM *To:* Chen Liang ; classfile-api-dev at openjdk.org < classfile-api-dev at openjdk.org> *Subject:* RE: [External] : more class file transformation questions Okay, a List sounds interesting. 1. Would I just use CodeModel::elementList() as my initial list or do I have to make a copy somehow? 2. How would I insert a new instruction into this list? Builders don?t seem to return an item, perhaps I need to use the static instruction of() methods? 3. What is difference between Bound and Unbound instructions? Thank you, Mark *From:* Chen Liang *Sent:* Tuesday, November 19, 2024 3:25 PM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Subject:* Re: [External] : more class file transformation questions Hi Mark, ClassFileBuilder::with takes an element object, such as a FieldModel for a ClassBuilder. It does not modify the object when applied to the new builder, except implementation details like constant pool references.. ClassFileBuilder::transform takes the same type of the object as the object to build, such as a ClassModel for a ClassBuilder. In the same time, it allows you to process all element objects of that same type of object. For example, a ClassBuilder can with a method or a field. A ClassBuilder can transform a class, and use this to merge classes, like clb.transform(sourceClassA, ClassTransform.ACCEPT_ALL).transform(sourceClassB, ClassTransform.ACCEPT_ALL) to merge source classes A and B into the ongoing class builder. For your specific use case, I think what you need is a method transform that does something like: (mb, me) -> { if (mb instanceof CodeModel code) { List codeElements = ...// prepare your list of instructions mb.withCode(codeElements::forEach); // the list of instructions get sent to the code builderOK } else { mb.with(me); } } It is also possible to do this in CodeTransform by collecting everything in a list in accept(CodeBuilder, CodeElement) and only working on the builder in atEnd(CodeBuilder), when you can see all the elements. But that code will be a bit not straightforward compared to this pattern I am showing off, and this CodeTransform can only see all elements that is from one original model instead of those from all original models (applying the merge class A and B example). P.S. When you reply, remember to choose "reply all" so that your reply gets sent to the classfile-api-dev list too. Regards, Chen Liang ------------------------------ *From:* Mark Roberts *Sent:* Tuesday, November 19, 2024 4:43 PM *To:* Chen Liang *Subject:* [External] : more class file transformation questions Thank you, those suggestions really helped! Next question, given a builder, I?m curious about the differences between the ?with? versus ?transform? methods. I see how the transforms can simplify coding and they support composition. The implication seems to be that with and build create new objects from old while transform modifies existing objects. But based on the examples I?ve seen they look very similar. Are there significant performance differences? Or other advantages? My use case, adding instrumentation code to class files, requires inspecting every byte code and possibly modifying a lot of them. I worry that I would have to composite many transforms where one linear walk through the byte codes would be sufficient. Am I missing something? (it wouldn?t surprise me if I was) Thank you, Mark *From:* Chen Liang *Sent:* Sunday, November 17, 2024 4:59 PM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Subject:* Re: [External] : RE: class file transformation questions Hi Mark, For performing a lot of modifications, you first can create a private static void check(CodeBuilder builder, CodeElement element) { // check the elements, and if it matches a pattern, call another method // as long as you pass the CodeBuilder and make changes to it, your changes will be saved } And then refer to this as a CodeTransform in a method reference. Or, if you want to do a huge overhaul to a CodeModel, you can get a MethodTransform, and have a specific method like: if (me instanceof CodeModel code) { methodBuilder.withCode(cob -> rebuildCode(cob, code)); } private static void rebuildCode(CodeBuilder builder, CodeModel model) {} This gives you a more holistic view of the code, and should be more friendly for massive transformations that almost rebuild the method code. (This one also allows you to pull instructions to a list with new ArrayList<>(model.elementList()), modify that list, and do list.forEach(builder) to send the results, which is used in some older patterns) Regards, Chen Liang ------------------------------ *From:* Mark Roberts *Sent:* Sunday, November 17, 2024 6:30 PM *To:* Chen Liang *Subject:* [External] : RE: class file transformation questions Thank you for your help, it is much appreciated. I have what I guess you would call a coding style question. If you wish to perform a lot of modifications to a class file I could envision a method with hundreds of lines of code. For a smaller example, see the chaining multiple transformations example in java/lang/classfile/ClassFileTransform.html. I find this very (extremely?) difficult to read. Is there any way of writing this code in a more ?traditional? way with several, smaller method bodies? Or is there no way to use java.lang.classfile without chaining together large numbers of builders written as lambda functions? Thank you, Mark *From:* Chen Liang *Sent:* Friday, November 15, 2024 4:48 PM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Subject:* Re: class file transformation questions Hi Mark, 1. For injecting code, you can override the atStart of the CodeTransform interface. Unfortunately we don't have a utility method for you to pass a lambda ? but we can always add it later. If you wish to find an injection point, you can get the full list of code elements, and find your insertion point and add the desired instruction objects. 2. For adding new methods, you can also override the atStart/atEnd method of your ClassTransform, or do andThen(ClassTransform.endHandler(clb -> clb.withMethod(...))) to add methods. 3. To duplicate an existing method object, you can first call withMethod to create a new method, and once you have the method builder, it has a transform(otherMethod, transform) that can pipe otherMethod's contents through the transform, and finally everything goes to the method builder. Feel free to ask more or suggest. Unfortunately I am still trying to improve documentations for ClassFile API, so at this stage the documents may still be insufficient, and many of the useful methods are not covered by the JEP. Regards, Chen Liang ------------------------------ *From:* classfile-api-dev on behalf of Mark Roberts *Sent:* Friday, November 15, 2024 1:42 PM *To:* classfile-api-dev at openjdk.org *Subject:* class file transformation questions Several of our tools use the java.lang.instrument package to instrument class files when they are loaded. We currently use BCEL to do the instrumentation, but would like to move to the new java.lang.classfile package. I have gotten some basic transforms working in this environment and I see how to modify existing instructions. However, we need to perform some larger modifications to the classes we instrument. There are three basic transforms we need to perform (all on the same class file): 1. Injecting code into an existing method 2. Adding new methods 3. Duplicating an existing method with some changes Any suggestions as to how to accomplish these tasks would be much appreciated. Thank you, Mark Roberts -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Wed Jan 29 09:07:19 2025 From: adam.sotona at oracle.com (Adam Sotona) Date: Wed, 29 Jan 2025 09:07:19 +0000 Subject: StackMap problem In-Reply-To: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> Message-ID: Hi Mark, Both attached class listings are missing stack map frames at branch targets, and they seem to be invalid. I would like to ask what compiler generates the first class and how your tool adds the instrumentation code. My first impression is that I don?t see any trace of Class-File API stack map generator involment. Transformation of a method with missing or invalid stack map would generate new stack maps, unless explicitly disabled. Thanks, Adam From: classfile-api-dev on behalf of Mark Roberts Date: Tuesday, 28 January 2025 at 21:45 To: classfile-api-dev at openjdk.org Cc: Chen Liang Subject: StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Wed Jan 29 16:45:14 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Wed, 29 Jan 2025 08:45:14 -0800 Subject: StackMap problem In-Reply-To: References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> Message-ID: <1187ae0f2788c3ae51a28b15f3e31719@mail.gmail.com> The ?before? was compiled with javac version 24-ea+22-269, but I compiled with javac version 17.0.11 and got the exact same binary. The stack map frames are there: StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=19, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) The first (@19) is the target of the exception handler, the second (@20) is the target of the branch @16. It all looks good to me. Mark *From:* Adam Sotona *Sent:* Wednesday, January 29, 2025 1:07 AM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Cc:* Chen Liang *Subject:* Re: StackMap problem Hi Mark, Both attached class listings are missing stack map frames at branch targets, and they seem to be invalid. I would like to ask what compiler generates the first class and how your tool adds the instrumentation code. My first impression is that I don?t see any trace of Class-File API stack map generator involment. Transformation of a method with missing or invalid stack map would generate new stack maps, unless explicitly disabled. Thanks, Adam *From: *classfile-api-dev on behalf of Mark Roberts *Date: *Tuesday, 28 January 2025 at 21:45 *To: *classfile-api-dev at openjdk.org *Cc: *Chen Liang *Subject: *StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Wed Jan 29 17:35:38 2025 From: adam.sotona at oracle.com (Adam Sotona) Date: Wed, 29 Jan 2025 17:35:38 +0000 Subject: [External] : RE: StackMap problem In-Reply-To: <1187ae0f2788c3ae51a28b15f3e31719@mail.gmail.com> References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> <1187ae0f2788c3ae51a28b15f3e31719@mail.gmail.com> Message-ID: I see, the frame is listed at the end of the line. Key question still remains ? how the instrumentation is done? Class- File API regenerates the stack map when I create similar method and inject the istore_1 and iload_1 instructions. Adam From: Mark Roberts Date: Wednesday, 29 January 2025 at 17:45 To: Adam Sotona , classfile-api-dev at openjdk.org Cc: Chen Liang Subject: [External] : RE: StackMap problem The ?before? was compiled with javac version 24-ea+22-269, but I compiled with javac version 17.0.11 and got the exact same binary. The stack map frames are there: StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=19, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) The first (@19) is the target of the exception handler, the second (@20) is the target of the branch @16. It all looks good to me. Mark From: Adam Sotona > Sent: Wednesday, January 29, 2025 1:07 AM To: Mark Roberts >; classfile-api-dev at openjdk.org Cc: Chen Liang > Subject: Re: StackMap problem Hi Mark, Both attached class listings are missing stack map frames at branch targets, and they seem to be invalid. I would like to ask what compiler generates the first class and how your tool adds the instrumentation code. My first impression is that I don?t see any trace of Class-File API stack map generator involment. Transformation of a method with missing or invalid stack map would generate new stack maps, unless explicitly disabled. Thanks, Adam From: classfile-api-dev > on behalf of Mark Roberts > Date: Tuesday, 28 January 2025 at 21:45 To: classfile-api-dev at openjdk.org > Cc: Chen Liang > Subject: StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jan 29 17:44:36 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 29 Jan 2025 17:44:36 +0000 Subject: [External] : RE: StackMap problem In-Reply-To: References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> <1187ae0f2788c3ae51a28b15f3e31719@mail.gmail.com> Message-ID: Indeed, I noted that our StackMapGenerator eagerly checks if a newly encountered store instruction (the istore_1 in the exception handler range) breaks the stack maps, and our StackMapGenerator would have failed fast in that case. Chen ________________________________ From: Adam Sotona Sent: Wednesday, January 29, 2025 11:35 AM To: Mark Roberts ; classfile-api-dev at openjdk.org Cc: Chen Liang Subject: Re: [External] : RE: StackMap problem I see, the frame is listed at the end of the line. Key question still remains ? how the instrumentation is done? Class- File API regenerates the stack map when I create similar method and inject the istore_1 and iload_1 instructions. Adam From: Mark Roberts Date: Wednesday, 29 January 2025 at 17:45 To: Adam Sotona , classfile-api-dev at openjdk.org Cc: Chen Liang Subject: [External] : RE: StackMap problem The ?before? was compiled with javac version 24-ea+22-269, but I compiled with javac version 17.0.11 and got the exact same binary. The stack map frames are there: StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=19, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) The first (@19) is the target of the exception handler, the second (@20) is the target of the branch @16. It all looks good to me. Mark From: Adam Sotona > Sent: Wednesday, January 29, 2025 1:07 AM To: Mark Roberts >; classfile-api-dev at openjdk.org Cc: Chen Liang > Subject: Re: StackMap problem Hi Mark, Both attached class listings are missing stack map frames at branch targets, and they seem to be invalid. I would like to ask what compiler generates the first class and how your tool adds the instrumentation code. My first impression is that I don?t see any trace of Class-File API stack map generator involment. Transformation of a method with missing or invalid stack map would generate new stack maps, unless explicitly disabled. Thanks, Adam From: classfile-api-dev > on behalf of Mark Roberts > Date: Tuesday, 28 January 2025 at 21:45 To: classfile-api-dev at openjdk.org > Cc: Chen Liang > Subject: StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From adam.sotona at oracle.com Wed Jan 29 18:02:11 2025 From: adam.sotona at oracle.com (Adam Sotona) Date: Wed, 29 Jan 2025 18:02:11 +0000 Subject: [External] : RE: StackMap problem In-Reply-To: References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> <1187ae0f2788c3ae51a28b15f3e31719@mail.gmail.com> Message-ID: I see what happened here. The transformed method is invalid because the istore_1 is injected into the try block and consumed outside. As I used CodeTransform::atStart to inject the istore_1 ? I injected it before the try block and stack maps are valid. It is not guaranteed Class-File API will always fail eagerly when generating stack maps for invalid bytecode. ClassFile::verify is recommended to verify the generated class file is valid. Your instrumentation should inject code introducing new locals before any try block (if consumed outside of the try block). Otherwise valid stack maps are unable to compute. Adam From: Chen Liang Date: Wednesday, 29 January 2025 at 18:44 To: Adam Sotona , classfile-api-dev at openjdk.org Subject: Re: [External] : RE: StackMap problem Indeed, I noted that our StackMapGenerator eagerly checks if a newly encountered store instruction (the istore_1 in the exception handler range) breaks the stack maps, and our StackMapGenerator would have failed fast in that case. Chen ________________________________ From: Adam Sotona Sent: Wednesday, January 29, 2025 11:35 AM To: Mark Roberts ; classfile-api-dev at openjdk.org Cc: Chen Liang Subject: Re: [External] : RE: StackMap problem I see, the frame is listed at the end of the line. Key question still remains ? how the instrumentation is done? Class- File API regenerates the stack map when I create similar method and inject the istore_1 and iload_1 instructions. Adam From: Mark Roberts Date: Wednesday, 29 January 2025 at 17:45 To: Adam Sotona , classfile-api-dev at openjdk.org Cc: Chen Liang Subject: [External] : RE: StackMap problem The ?before? was compiled with javac version 24-ea+22-269, but I compiled with javac version 17.0.11 and got the exact same binary. The stack map frames are there: StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=19, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) The first (@19) is the target of the exception handler, the second (@20) is the target of the branch @16. It all looks good to me. Mark From: Adam Sotona > Sent: Wednesday, January 29, 2025 1:07 AM To: Mark Roberts >; classfile-api-dev at openjdk.org Cc: Chen Liang > Subject: Re: StackMap problem Hi Mark, Both attached class listings are missing stack map frames at branch targets, and they seem to be invalid. I would like to ask what compiler generates the first class and how your tool adds the instrumentation code. My first impression is that I don?t see any trace of Class-File API stack map generator involment. Transformation of a method with missing or invalid stack map would generate new stack maps, unless explicitly disabled. Thanks, Adam From: classfile-api-dev > on behalf of Mark Roberts > Date: Tuesday, 28 January 2025 at 21:45 To: classfile-api-dev at openjdk.org > Cc: Chen Liang > Subject: StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jan 29 18:07:58 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 29 Jan 2025 18:07:58 +0000 Subject: [External] : labels In-Reply-To: References: Message-ID: Hi Mark, An approach can be to use a List. When you process each item, you pattern match like: switch(item) { case LabelTarget lt -> {} // drop case CodeElement coe -> cob.with(coe); case Label l -> cob.labelBinding(l); } I think that can solve your issue. And I see the transform problem with stack maps is caused by your inability to handle labels. This should be able to avoid those issues in the future. Regards, Chen Liang -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Wed Jan 29 18:26:54 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Wed, 29 Jan 2025 10:26:54 -0800 Subject: [External] : RE: StackMap problem In-Reply-To: References: <7d6f28224119fe334f0a520582e9ef95@mail.gmail.com> <1187ae0f2788c3ae51a28b15f3e31719@mail.gmail.com> Message-ID: Oh I see ? I should have changed the exception handler range to 17-33. This relates to my problem adding new labels that Chen has addressed in another thread. Thanks *From:* classfile-api-dev *On Behalf Of *Adam Sotona *Sent:* Wednesday, January 29, 2025 10:02 AM *To:* Chen Liang ; classfile-api-dev at openjdk.org *Subject:* Re: [External] : RE: StackMap problem I see what happened here. The transformed method is invalid because the istore_1 is injected into the try block and consumed outside. As I used CodeTransform::atStart to inject the istore_1 ? I injected it before the try block and stack maps are valid. It is not guaranteed Class-File API will always fail eagerly when generating stack maps for invalid bytecode. ClassFile::verify is recommended to verify the generated class file is valid. Your instrumentation should inject code introducing new locals before any try block (if consumed outside of the try block). Otherwise valid stack maps are unable to compute. Adam *From: *Chen Liang *Date: *Wednesday, 29 January 2025 at 18:44 *To: *Adam Sotona , classfile-api-dev at openjdk.org < classfile-api-dev at openjdk.org> *Subject: *Re: [External] : RE: StackMap problem Indeed, I noted that our StackMapGenerator eagerly checks if a newly encountered store instruction (the istore_1 in the exception handler range) breaks the stack maps, and our StackMapGenerator would have failed fast in that case. Chen ------------------------------ *From:* Adam Sotona *Sent:* Wednesday, January 29, 2025 11:35 AM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Cc:* Chen Liang *Subject:* Re: [External] : RE: StackMap problem I see, the frame is listed at the end of the line. Key question still remains ? how the instrumentation is done? Class- File API regenerates the stack map when I create similar method and inject the istore_1 and iload_1 instructions. Adam *From: *Mark Roberts *Date: *Wednesday, 29 January 2025 at 17:45 *To: *Adam Sotona , classfile-api-dev at openjdk.org < classfile-api-dev at openjdk.org> *Cc: *Chen Liang *Subject: *[External] : RE: StackMap problem The ?before? was compiled with javac version 24-ea+22-269, but I compiled with javac version 17.0.11 and got the exact same binary. The stack map frames are there: StackMapTable: StackMap((SAME_LOCALS_1_STACK, offset delta=19, stack items={(type=Object, class=DataStructures.Overflow)}), (SAME, offset delta=0)) The first (@19) is the target of the exception handler, the second (@20) is the target of the branch @16. It all looks good to me. Mark *From:* Adam Sotona *Sent:* Wednesday, January 29, 2025 1:07 AM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Cc:* Chen Liang *Subject:* Re: StackMap problem Hi Mark, Both attached class listings are missing stack map frames at branch targets, and they seem to be invalid. I would like to ask what compiler generates the first class and how your tool adds the instrumentation code. My first impression is that I don?t see any trace of Class-File API stack map generator involment. Transformation of a method with missing or invalid stack map would generate new stack maps, unless explicitly disabled. Thanks, Adam *From: *classfile-api-dev on behalf of Mark Roberts *Date: *Tuesday, 28 January 2025 at 21:45 *To: *classfile-api-dev at openjdk.org *Cc: *Chen Liang *Subject: *StackMap problem The existence of compiler generated, unnamed local temps (no LocalVariable code element) seems to cause a problem with stack maps. The ?source? attachment shows the problem method. The ?before? attachment shows the generated code before instrumentation. Note that the compiler generates an unnamed temp at slot 0 to store the exception code. The stackmap for the branch target says same locals, which is correct. The ?after? attachment shows the code generated after our tool has added instrumentation code ? note the addition of a new local variable at slot 1. The stackmap for the branch target still says same locals, which is no longer correct. This causes the iload_1 @41 to fail. I tried to see if this issue has already been reported, but I was unable to figure out how to search https://bugs.java.com/bugdatabase/ by keyword. Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From markro at cs.washington.edu Thu Jan 30 02:20:16 2025 From: markro at cs.washington.edu (Mark Roberts) Date: Wed, 29 Jan 2025 18:20:16 -0800 Subject: [External] : labels In-Reply-To: References: Message-ID: <33527cd2a8a1383dc9bf12d390c878b8@mail.gmail.com> Rather than change List to List to allow labels to be included, I made a HashMap to associate a Label with the appropriate CodeElement. When doing the cob.with(coe) I check the map and do a cob.labelBinding() as well, if needed. Works nicely and made it fairly easy to solve the ExceptionCatch problem. Thanks to all for your help. Mark *From:* Chen Liang *Sent:* Wednesday, January 29, 2025 10:08 AM *To:* Mark Roberts ; classfile-api-dev at openjdk.org *Subject:* Re: [External] : labels Hi Mark, An approach can be to use a List. When you process each item, you pattern match like: switch(item) { case LabelTarget lt -> {} // drop case CodeElement coe -> cob.with(coe); case Label l -> cob.labelBinding(l); } I think that can solve your issue. And I see the transform problem with stack maps is caused by your inability to handle labels. This should be able to avoid those issues in the future. Regards, Chen Liang -------------- next part -------------- An HTML attachment was scrubbed... URL: