Crazy hack - turn off checked exceptions in javac.

1111 views
Skip to first unread message

Reinier Zwitserloot

unread,
Aug 20, 2009, 6:42:12 PM8/20/09
to The Java Posse
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.

Mark Derricutt

unread,
Aug 20, 2009, 6:53:36 PM8/20/09
to java...@googlegroups.com
I love the idea!  For a long time I'd thought I'd love checked exceptions to actually just be compiler warnings, that one could enable the enforcement of, or as in the case of lombok - unenforce.

Mark

--

Christian Catchpole

unread,
Aug 20, 2009, 7:25:43 PM8/20/09
to The Java Posse
Cool. Looks like the right approach to me. The problem I have with
'just recompiling javac' is that it's not very friendly in a typical
dev environment.

1. I have to tell my boss we are using a 'custom' java compiler.
2. I have to make sure that is 'patched' everywhere. On dev machines
and on CI servers etc.
3. When someone downloads the latest JDK, a re-patch is required. It
increases the risk of getting 'stuck on a version'.

The Lombok approach works because it can be seen as just another
dependency to your project. Not hacky shenanigans. If the trickery
happens at runtime it makes your build process repeatable and
predictable.

Casper Bang

unread,
Aug 20, 2009, 7:42:23 PM8/20/09
to The Java Posse
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

Reinier Zwitserloot

unread,
Aug 20, 2009, 9:38:45 PM8/20/09
to The Java Posse
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

Casper Bang

unread,
Aug 20, 2009, 11:40:08 PM8/20/09
to The Java Posse
Ah ok, you are short-circuiting isUnchecked. As I've since learned and
you too it appears, that's an extremely invasive (and probably
erroneous) approach. I've outlined how I tweaked javac leniency in a
more graceful (converted errors to warnings) fashion here:
http://coffeecokeandcode.blogspot.com/2009/08/tweaking-javac-leniency.html

My next step was to add command line configurations. It's too late for
me to fathom how you would do that via Lombok but I agree, mangling
around with -D switches ain't pretty. Keep us posted though.

/Casper
> http://github.com/rzwitserloot/lombok/blob/172958ac099aa7bb6cef140cc8...
>
> http://github.com/rzwitserloot/lombok/blob/172958ac099aa7bb6cef140cc8...

Азис Мразиш

unread,
Feb 3, 2013, 2:56:53 PM2/3/13
to java...@googlegroups.com
Guys, it seems there is a legal way to disable checked exceptions in Eclipse and maven, if you still interested.

1) patch eclipse jdt compiler: shortcut org.eclipse.jdt.internal.compiler.problem.ProblemReporter.unhandledException()
2) build jdt and eclipse with that change (thought, i made that change in jar using bytecode editor and it worked fine for me)
3) use maven jdt compiler plugin 
4) profit !
Reply all
Reply to author
Forward
0 new messages