From szaldana at redhat.com Wed Sep 28 15:51:55 2022 From: szaldana at redhat.com (Sonia Zaldana Calles) Date: Wed, 28 Sep 2022 11:51:55 -0400 Subject: Class.forName Bytecode Transformation Investigation Message-ID: Hi folks, In line with Dan?s previous investigation on bytecode transformations to modify Java?s dynamic behaviour, I have created a ClassForName JLink Plugin which translates the reflective operation Class.forName to the ldc bytecode instruction using the ASM library. The bytecode transformation switches out calls to Class.forName to ldc instructions for classes that are statically known, along with addressing the mismatch between Class.forName, which initialises the class by default, and ldc, which does not, by using MethodHandles.Lookup.ensureInitialized . For example, the bytecode sequence Class.forName(?com.sonia.hello.HelloWorld?) is transformed as shown below: Original: 0: ldc #7 // String com.sonia.hello.HelloWorld 2: invokestatic #9 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; Transformed: 0: invokestatic #43 // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; 3: ldc #45 // class com/sonia/hello/HelloWorld 5: invokevirtual #51 // Method java/lang/invoke/MethodHandles$Lookup.ensureInitialized:(Ljava/lang/Class;)Ljava/lang/Class; The plugin supports configuring two modes: global and module-aware. The module mode transforms Class.forName calls only when accessing a class accessible to the current module per the module graph, while the global mode transforms all classes regardless of readability. Note, class accessibility is determined per the JVMS 5.4.4 - a class must be public, its module must be read by the current class?s module and its package must be exported to the current class?s module. There are slight differences between the language?s and the VM?s view of accessibility. We have also introduced an internal plugin called ModuleGraphPlugin which creates a single logical view of the module graph. This plugin separates the logic determining class readability and is queried by the ClassForName plugin to drive transformations. It doesn?t require further configuration from users as it is configured by default when using the ClassForName plugin. The ModuleGraphPlugin was separated out as it allows other plugins to share a common view of the module graph without needing to have each plugin process the module-info.java files. While a Class.forName JLink plugin previously existed (and was removed), it was limited to modifications within a module as it didn?t have the ModuleGraphPlugin-awareness of the full module graph. We don?t expect any performance improvement from this change itself, but we are expecting it to increase the ability of other future plugins to reason about application behaviour and to potentially help remove reflective operations in other contexts. Find below the GitHub link with the changes: https://github.com/SoniaZaldana/jdk-sandbox/tree/classforname-plugin Thanks, Sonia -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Sep 28 16:59:03 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 28 Sep 2022 12:59:03 -0400 Subject: Class.forName Bytecode Transformation Investigation In-Reply-To: References: Message-ID: <695ab646-5a0b-4b9e-5825-6fa5a0eb2c28@oracle.com> One possible compatibility issue is the exceptions that may be thrown.? Have we done a review to compare the exceptions thrown by Class::forName vs the substituted code? On 9/28/2022 11:51 AM, Sonia Zaldana Calles wrote: > Hi folks, > > In line with Dan?s previous investigation on bytecode transformations > to modify Java?s dynamic behaviour, I have created a ClassForName > JLink Plugin which translates the reflective operation Class.forName > to > the ldc bytecode instruction using the ASM library. > > > The bytecode transformation switches out calls to Class.forName to ldc > instructions for classes that are statically known, along with > addressing the mismatch between Class.forName, which initialises the > class by default, and ldc, which does not, by using > MethodHandles.Lookup.ensureInitialized > . > > > > For example, the bytecode sequence > Class.forName(?com.sonia.hello.HelloWorld?) is transformed as shown > below: > > > Original: > > 0: ldc ? ? ? ? ? #7? ? ? ? ? ? ? ? ? // String com.sonia.hello.HelloWorld > > 2: invokestatic? #9? ? ? ? ? ? ? ? ? // Method > java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; > > > Transformed: > > 0: invokestatic? #43 // Method > java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; > > 3: ldc ? ? ? ? ? #45 ? ? ? ? ? ? ? ? // class com/sonia/hello/HelloWorld > > 5: invokevirtual #51 // Method > java/lang/invoke/MethodHandles$Lookup.ensureInitialized:(Ljava/lang/Class;)Ljava/lang/Class; > > > > The plugin supports configuring two modes: global and module-aware. > The module mode transforms Class.forName calls only when accessing a > class accessible to the current module per the module graph, while the > global mode transforms all classes regardless of readability. Note, > class accessibility is determined per the JVMS 5.4.4 > - > a class must be public, its module must be read by the current class?s > module and its package must be exported to the current class?s > module.? There are slight differences between the language?s and the > VM?s view of accessibility. > > > We have also introduced an internal plugin called ModuleGraphPlugin > which creates a single logical view of the module graph. This plugin > separates the logic determining class readability and is queried by > the ClassForName plugin to drive transformations. It doesn?t require > further configuration from users as it is configured by default when > using the ClassForName plugin. > > > The ModuleGraphPlugin was separated out as it allows other plugins to > share a common view of the module graph without needing to have each > plugin process the module-info.java files. > > > While a Class.forName JLink plugin previously existed (and was > removed), it was limited to modifications within a module as it didn?t > have the ModuleGraphPlugin-awareness of the full module graph. > > > We don?t expect any performance improvement from this change itself, > but we are expecting it to increase the ability of other future > plugins to reason about application behaviour and to potentially help > remove reflective operations in other contexts. > > > Find below the GitHub link with the changes: > > https://github.com/SoniaZaldana/jdk-sandbox/tree/classforname-plugin > > > > Thanks, > > Sonia > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Sep 28 17:27:59 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 28 Sep 2022 13:27:59 -0400 Subject: Class.forName Bytecode Transformation Investigation In-Reply-To: References: Message-ID: <868fb1b0-d064-f3be-f6d9-6b7a46ad18c1@oracle.com> Does your plugin eliminate dead reflective exception handlers?? If the class is known to be in the image, then we can not only transform Class::forName, but handling of attendant CNFE as well.? In: ??? try { ??????? c = Class.forName("blah"); ??? } ??? catch (CFNE ce) { /* stuff */ } the catch handler is also dead if the class is known to be in the image. On 9/28/2022 11:51 AM, Sonia Zaldana Calles wrote: > Hi folks, > > In line with Dan?s previous investigation on bytecode transformations > to modify Java?s dynamic behaviour, I have created a ClassForName > JLink Plugin which translates the reflective operation Class.forName > to > the ldc bytecode instruction using the ASM library. > > > The bytecode transformation switches out calls to Class.forName to ldc > instructions for classes that are statically known, along with > addressing the mismatch between Class.forName, which initialises the > class by default, and ldc, which does not, by using > MethodHandles.Lookup.ensureInitialized > . > > > > For example, the bytecode sequence > Class.forName(?com.sonia.hello.HelloWorld?) is transformed as shown > below: > > > Original: > > 0: ldc ? ? ? ? ? #7? ? ? ? ? ? ? ? ? // String com.sonia.hello.HelloWorld > > 2: invokestatic? #9? ? ? ? ? ? ? ? ? // Method > java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; > > > Transformed: > > 0: invokestatic? #43 // Method > java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; > > 3: ldc ? ? ? ? ? #45 ? ? ? ? ? ? ? ? // class com/sonia/hello/HelloWorld > > 5: invokevirtual #51 // Method > java/lang/invoke/MethodHandles$Lookup.ensureInitialized:(Ljava/lang/Class;)Ljava/lang/Class; > > > > The plugin supports configuring two modes: global and module-aware. > The module mode transforms Class.forName calls only when accessing a > class accessible to the current module per the module graph, while the > global mode transforms all classes regardless of readability. Note, > class accessibility is determined per the JVMS 5.4.4 > - > a class must be public, its module must be read by the current class?s > module and its package must be exported to the current class?s > module.? There are slight differences between the language?s and the > VM?s view of accessibility. > > > We have also introduced an internal plugin called ModuleGraphPlugin > which creates a single logical view of the module graph. This plugin > separates the logic determining class readability and is queried by > the ClassForName plugin to drive transformations. It doesn?t require > further configuration from users as it is configured by default when > using the ClassForName plugin. > > > The ModuleGraphPlugin was separated out as it allows other plugins to > share a common view of the module graph without needing to have each > plugin process the module-info.java files. > > > While a Class.forName JLink plugin previously existed (and was > removed), it was limited to modifications within a module as it didn?t > have the ModuleGraphPlugin-awareness of the full module graph. > > > We don?t expect any performance improvement from this change itself, > but we are expecting it to increase the ability of other future > plugins to reason about application behaviour and to potentially help > remove reflective operations in other contexts. > > > Find below the GitHub link with the changes: > > https://github.com/SoniaZaldana/jdk-sandbox/tree/classforname-plugin > > > > Thanks, > > Sonia > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From szaldana at redhat.com Wed Sep 28 18:01:32 2022 From: szaldana at redhat.com (Sonia Zaldana Calles) Date: Wed, 28 Sep 2022 14:01:32 -0400 Subject: Class.forName Bytecode Transformation Investigation In-Reply-To: <868fb1b0-d064-f3be-f6d9-6b7a46ad18c1@oracle.com> References: <868fb1b0-d064-f3be-f6d9-6b7a46ad18c1@oracle.com> Message-ID: Hi Brian, That?s a great catch. We need to ensure we are preserving the meaning of the code and the plugin is not removing dead exception handlers. I?ll look into this. Thanks, Sonia On Wed, Sep 28, 2022 at 1:28 PM Brian Goetz wrote: > Does your plugin eliminate dead reflective exception handlers? If the > class is known to be in the image, then we can not only transform > Class::forName, but handling of attendant CNFE as well. In: > > try { > c = Class.forName("blah"); > } > catch (CFNE ce) { /* stuff */ } > > the catch handler is also dead if the class is known to be in the image. > > On 9/28/2022 11:51 AM, Sonia Zaldana Calles wrote: > > Hi folks, > > In line with Dan?s previous investigation on bytecode transformations to > modify Java?s dynamic behaviour, I have created a ClassForName JLink Plugin > which translates the reflective operation Class.forName > > to the ldc bytecode instruction using the ASM > library. > > The bytecode transformation switches out calls to Class.forName to ldc > instructions for classes that are statically known, along with addressing > the mismatch between Class.forName, which initialises the class by default, > and ldc, which does not, by using MethodHandles.Lookup.ensureInitialized > > . > > For example, the bytecode sequence > Class.forName(?com.sonia.hello.HelloWorld?) is transformed as shown below: > > Original: > > 0: ldc #7 // String com.sonia.hello.HelloWorld > > 2: invokestatic #9 // Method > java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class; > > Transformed: > > 0: invokestatic #43 // Method > java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; > > 3: ldc #45 // class com/sonia/hello/HelloWorld > > 5: invokevirtual #51 // Method > java/lang/invoke/MethodHandles$Lookup.ensureInitialized:(Ljava/lang/Class;)Ljava/lang/Class; > > > The plugin supports configuring two modes: global and module-aware. The > module mode transforms Class.forName calls only when accessing a class > accessible to the current module per the module graph, while the global > mode transforms all classes regardless of readability. Note, class > accessibility is determined per the JVMS 5.4.4 > > - a class must be public, its module must be read by the current class?s > module and its package must be exported to the current class?s module. > There are slight differences between the language?s and the VM?s view of > accessibility. > > We have also introduced an internal plugin called ModuleGraphPlugin which > creates a single logical view of the module graph. This plugin separates > the logic determining class readability and is queried by the ClassForName > plugin to drive transformations. It doesn?t require further configuration > from users as it is configured by default when using the ClassForName > plugin. > > The ModuleGraphPlugin was separated out as it allows other plugins to > share a common view of the module graph without needing to have each plugin > process the module-info.java files. > > While a Class.forName JLink plugin previously existed (and was removed), > it was limited to modifications within a module as it didn?t have the > ModuleGraphPlugin-awareness of the full module graph. > > We don?t expect any performance improvement from this change itself, but > we are expecting it to increase the ability of other future plugins to > reason about application behaviour and to potentially help remove > reflective operations in other contexts. > > Find below the GitHub link with the changes: > > https://github.com/SoniaZaldana/jdk-sandbox/tree/classforname-plugin > > > Thanks, > > Sonia > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: