Paullo612 / mlfx

OpenJFX's FXML language AOT compler
Apache License 2.0
12 stars 0 forks source link

How to get rid of Micronaut processor #34

Open ennerf opened 1 year ago

ennerf commented 1 year ago

Micronaut-inject generates a ton extra $Introspection and $Definition classes that bloat the binary and seem to produce some problems with ProGuard obfuscation.

Making the fxml fields/methods public generates byte code that doesn't depend on Micronaut introspection, but the classes still get generated. Is there a way to run only the fxml annotation processor? I tried removing the micronaut-inject-java annotation processor, but that results in the fxml files not being generated either.

Fwiw, I did a quick test yesterday to automatically generate GraalVM reflect-config files for annotated fields (see ConfigGenerator) and it turned out to be pretty straight forward. It might make sense to have a low-dependency mode that uses public access and falls back to reflection w/ generated config files.

Paullo612 commented 1 year ago

Yes, it's a great idea, but not quite straightforward to implement. compiler-core can mark fields and methods that require reflections with special annotation, and then compiler.micronaut can skip generating introspection for controllers that does not require reflections. The only show stopper now is controller construction logic. If you do not have custom controller factory, controllers are created using Micronaut's introspector. To work-around this, new factory method that constructs controller or throws if there is no accessible no-args constructor can be added to CompiledFXMLLoader.

Reflective implementation is also possible, but I haven't implemented it because it's not clear to me how to deal with Jigsaw modules. Should this implementation be in automatic module, or should there be compiled module-info patcher? To implement MLFXLoaderDelegate there should be some kind of cache of available loaders at runtime. This cache can be implemented using ServiceLoader, but that would require tonns of provides io.github.paullo612.mlfx.api.CompiledFXMLLoader with ... in your module-info.

For the reference: there is reflection based accessor implementation on Groovy for tests.

Paullo612 commented 1 year ago

About $Definition files: Looks like you're using some sort of DI in your app. I found no way to stop Micronaut's annotation processor from generating bean definitions for now. Looks like I should ask Micronaut guys to provide one. For now, you can just delete those $Definition files with maven-clean-plugin on package phase. The only required files are ...$Introspection and ...$IntrospectionRef.

micronaut-inject-java cannot be removed from annotation processors classpath, because it provides AST abstraction implementation for Java.

ennerf commented 1 year ago

Right, I didn't consider the module-info. I rarely use reflection and usually create a shaded/obfuscated/shrunk jar that gets rid of the individual jigsaw modules, so I'm not familiar with how it works across modules. I think some amount of one-time user setup would be reasonable as long as it gets rid of the grunt work of running the agent on updates.

After looking at the setup a bit more, my understanding is that this project has 3 different annotation processors:

All of them get called by the annotation processor service in micronaut-inject-java, which also calls the micronaut processors for generating $Introspection and $Definition. I tried using only the compiler-core processor, but that still generates the definition classes. Unless there is some micronaut flag to disable it this looks harder to get rid of than I initially thought.

The ProGuard issue I ran into seems to come from the $CompiledFXMLLoader classes, but I can't say anything specific because the error is absolutely useless... I'll create an issue if I manage to reproduce it in a smaller example.

 [proguard] ProGuard, version 7.2.0
 [proguard] Unexpected error
 [proguard] java.lang.StringIndexOutOfBoundsException: String index out of range: 93
 [proguard]     at java.lang.StringLatin1.charAt(StringLatin1.java:48) ~[?:?]
 [proguard]     at java.lang.String.charAt(String.java:1512) ~[?:?]
 [proguard]     at proguard.classfile.util.DescriptorClassEnumeration.nextFluff(DescriptorClassEnumeration.java:171) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.DescriptorClassEnumeration.classCount(DescriptorClassEnumeration.java:66) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.ClassReferenceInitializer.findReferencedClasses(ClassReferenceInitializer.java:1320) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.ClassReferenceInitializer.visitLocalVariableTypeInfo(ClassReferenceInitializer.java:718) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.attribute.LocalVariableTypeTableAttribute.localVariablesAccept(LocalVariableTypeTableAttribute.java:73) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.ClassReferenceInitializer.visitLocalVariableTypeTableAttribute(ClassReferenceInitializer.java:609) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.attribute.LocalVariableTypeTableAttribute.accept(LocalVariableTypeTableAttribute.java:60) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.attribute.CodeAttribute.attributesAccept(CodeAttribute.java:236) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.ClassReferenceInitializer.visitCodeAttribute(ClassReferenceInitializer.java:595) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.attribute.CodeAttribute.accept(CodeAttribute.java:138) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.ProgramMethod.attributesAccept(ProgramMethod.java:148) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.ClassReferenceInitializer.visitProgramMethod(ClassReferenceInitializer.java:309) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.ProgramMethod.accept(ProgramMethod.java:140) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.ProgramClass.methodsAccept(ProgramClass.java:695) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.util.ClassReferenceInitializer.visitProgramClass(ClassReferenceInitializer.java:266) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.ProgramClass.accept(ProgramClass.java:544) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.classfile.ClassPool.classesAccept(ClassPool.java:291) ~[proguard-core-9.0.8.jar:9.0.8]
 [proguard]     at proguard.Initializer.execute(Initializer.java:187) ~[proguard-base-7.2.0.jar:7.2.0]
 [proguard]     at proguard.ProGuard.initialize(ProGuard.java:324) ~[proguard-base-7.2.0.jar:7.2.0]
 [proguard]     at proguard.ProGuard.execute(ProGuard.java:138) ~[proguard-base-7.2.0.jar:7.2.0]
 [proguard]     at proguard.ProGuard.main(ProGuard.java:675) [proguard-base-7.2.0.jar:7.2.0]
ennerf commented 1 year ago

Thanks for looking into it. My answer took too long to write, so I didn't see your comment until after.

I'm currently using Afterburner.fx for DI. It's very minimal and works quite well with JavaFX.