Here's the complete flow. Yes, it's a hack. A chain of things you're not supposed to do.
1. disableCheckedExceptions-alpha.jar (hereafter: dCE.jar) is an annotation processor. This is public API, and is done by adding a SPI file (META-INF/services/javax.annotation.Processor listing the classname of your processor. So far we're still in public API territory. 2. The processor gets initialized by javac. Still okay. But this is where we walk away from public API, and into the territory of things which you're not supposed to do: 3. The processor first forces class initialization of sun.instrument.InstrumentationImpl via Class.forName. If this fails, a warning message is emitted via the processor API and nothing else happens (you'd be using a non-sun/sun-derived Java VM. Both the OpenJDK and Apple VMs DO work with this jar, though). If it works, this causes (under the hood) the instrumentation system to load, which includes the loading of libinstrument.so/jnilib/dll, which is part of the sun JRE, so this is NOT a dependency on a native library. We need it to be loaded so we can start calling into it: 4. Using JNA, we act as if we are the JVMTI system and call into the C code that start injected java agents. Normally you do this by connecting something like jconsole to a running VM, and loading an agent that way. You can actually connect to your own VM, but initializing the management facilities in the middle of a VM run takes more than 3 seconds on my machine. I assume nobody in his right mind is willing to tolerate 3 seconds of delay anytime you start javac, which is why that solution isn't acceptable, and we fake it with a C call instead. That only adds about 170 milliseconds to the total time of javac, which seems acceptable. 5. We hand the C call to start an agent our own jar file - the interface requires a jar file as the agent class name is stored in the jar manifest. You can't actually know what your jar file is with public API, so we again hack it: We use Class.getResource() to get a URL to our own processor class, and we then regexp the jar part out of it, so we can hand that off to the C call (which is Agent_OnAttach, in case you were wondering). 6. The Agent_OnAttach call does its thing and our agent is called via agentmain. We get an Instrumentation object, which is the goal of this entire exercise. With it, we register a class transformer, and then use the new (since v1.6) reload class feature to cause com.sun.tools.javac.comp.Check to be reloaded. 7. As we're a registered class transformer now, we get the chance to modify the raw byte array of the com.sun.tools.javac.comp.Check class. We do this using the ASM library (from objectweb - excellent library to rewrite class files). Specifically, the isUnchecked(ClassSymbol) method is rewritten from whatever it is to: "return true;". That's it. The annotation processor's done its job and technically the agent and the processor can be unloaded at this point in time, though that's not what happens - all the actual action hooks just do nothing and return immediately. I don't think there's much value in unloading at this junction. I spent the least amount of time on doing that rewrite, the majority of the work is in letting an annotation processor load itself as an agent during init process. I just went off what you found out earlier, Casper. It would indeed be nice if the tool becomes configurable: - allow catching of unthrown exceptions: Error / Warning / Allow - allow throwing of undeclared checked exceptions: Error / Warning / Allow parameters could be transported to javac via -D switches, though I'd rather find a better way to configure this stuff. Eventually this stuff will find its way back into lombok, but until I figure out how to do this to eclipse, it'll remain a completely separate side thing. The code really isn't very big. It's just 2 java files, and some effort in loading the right SPI file and the right manifest in the build.xml. Here are links: http://github.com/rzwitserloot/lombok/blob/172958ac099aa7bb6cef140cc8b6192531e5cb88/src_disableCheckedExceptions/lombok/javac/disableCheckedExceptions/DynamicAgent.java http://github.com/rzwitserloot/lombok/blob/172958ac099aa7bb6cef140cc8b6192531e5cb88/src_disableCheckedExceptions/lombok/javac/disableCheckedExceptions/CheckForThrownExceptionTransformer.java if you want to hack away at it, git clone that project, then: git checkout disableCheckedExceptions On Aug 21, 1:42 am, Casper Bang <[email protected]> wrote: > Congrats, very cool, I had my doubts whether this was possible but am > happy to see it is. For mass consumption in an existing tool-chain, it > beats my hacking of javac. Was working on a blog entry to cover source > code javac short-circuiting, but I think I'll suspend it and dive into > this approach instead. > > Question, are you currently rewriting the isUnchecked(...) calls in > com.sun.tools.javac.comp.Check.class? If you want to convert the > unchecked exception from an error into a warning, you should be able > to do that by rewriting the com.sun.tools.javac.comp.Flow.class, in > the errorUncaught() method simply have it dispatch calls to log.warning > (...) rather than the current log.err(...). > > Note that if you try this, you will get warning from javac as it is > unable to now find the correct resource keys in > com.sun.tools.javac.resources.compiler.properties and a few more > elements to the ListResourceBundle in > com.sun.tools.javac.resources.compiler.class (although I suppose, at > the bytecode level, the former has been merged into the > compiler.class?). > > /Casper > > > > > After some coordinating with Perry Nguyen and Casper Bang, I now have > > created this thing: > > >http://projectlombok.org/disableCheckedExceptions.html > > > It simply disables checked exceptions, completely, in your javac. All > > you have to do is add it to the classpath as you compile, like so: > > > ~> javac -cp disableCheckedExceptions-alpha.jar *.java > > > That's a vanilla javac. The notion of checked exceptions is eliminated > > completely; you may throw any exception without declaring it, and you > > may catch any exception even if it is not thrown in the try body that > > goes with the catch block. You'll need a javac v1.6 for this to work, > > I doubt it'll fly under javac 1.5 (you'd at least have to add the jar > > as an annotation processor on 1.5, and even then I doubt it'll work). > > > A good idea? Well, who knows. Experiment to your hearts content and > > find out! > > > Unlike project lombok, this little side project won't work in eclipse > > or any other IDE. It's more a proof of concept that javac can be > > completely modified at will by an annotation processor if you try hard > > enough. This does mean that adding e.g. closures or anything else that > > requires grammar changes is technically doable as annotation > > processor. I already knew that was possible with eclipse and other > > IDEs. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "The Java Posse" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/javaposse?hl=en -~----------~----~----~----~------~----~------~--~---
