 |
Munge: Swing's Secret Preprocessor
Posted by tball on September 05, 2006 at 01:57 PM | Comments (7)
This may seem like ancient history now, but when Swing was first developed the team was sucked into a maelstrom of technical and corporate controversy. The biggest areas of contention were:
- It must be fully JDK 1.1-compatible, distributable as a separate library;
- It must make use of the new Java 2D and other interesting features in the upcoming Java 2 release;
- It had to be part of the core classes, since developers didn't trust that non-core classes would be available on all Java platforms;
- "Your VP promised us at JavaOne it would release in three months; where is it?"
JDK 1.1-compatibility plus Java 2D support (the first two items) posed a big problem because Swing needed to extend the Graphics class to cache its state for an essential performance gain, while Java2D's Graphics2D extends Graphics for other reasons. Since Java doesn't support multiple-inheritance, SwingGraphics in 1.2 had to extend Graphics2D, which didn't exist in 1.1. We tried dynamically loading two different extension classes, but found a ripple effect through our source as more and more classes that reference SwingGraphics had to be dynamically loaded as well.
This problem would be a no-brainer in C/C++: use its preprocessor. But back then one of Java's perceived advantages was that it didn't have a preprocessor, and it would have been politically-incorrect for the JDK team to then create one. A more pragmatic reason was that source was distributed and needed to be compilable without other tools, plus have full comments (cpp strips them). Another reason is that to meet our "three-month" release target, we usually skipping running make and instead ran the Java interpreter with the -checksource flag, which automatically recompiled as needed (I really miss that feature).
The other big issue was what Swing's package names should be: com.sun.swing.*, java.swing.*, javax.swing.*, some.other.wacky.name.*, etc. It sounds a bit silly today, but like a lot of engineering discussions this seemed like a life-and-death issue to many developers. People really wanted Swing in core but weren't will to wait for 1.2 to release (smart move, in hindsight ;-), and were leery about any name with "Sun" in it. Even the final names, javax.swing.*, were controversial because they didn't seem "core enough" to address everyone's concerns. Since Swing was releasing every week at that point, we wound up changing the package names several times.
So a combination preprocessor and string translator, Munge, was created to address these two problems (source). Since its requirements were that it be small, fast and require no maintenance (it wasn't a product, after all), it purposely has no features that weren't needed by the team. It won't have your favorite cpp or sed feature, but what it did, it did quickly and correctly. It wasn't open-source then, but if you wanted a feature you were handed the source and told to have fun.
Munge used an approach which is now fairly common: embed the preprocessor directives in comments so the source is still Java and can be compiled without modification. This means picking a "preferred" target, which in our case was the Java 1.1 API since we first shipped Swing for that platform. For example (from Java 1.2 SwingUtilities source), support for the Java 1.2 security API is conditionally referenced, so that only the "a doo run run" line (original comment) is executed by default:
final static void doPrivileged(final Runnable doRun) {
/*if[JDK1.2]
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
doRun.run();
return null;
}
}
);
else[JDK1.2]*/
doRun.run(); // ... "a doo run run".
/*end[JDK1.2]*/
}
The mystery of those gaps in the old Swing source files is now solved: Munge stripped out either the 1.1- or 1.2-specific code, leaving those gaps so the line numbers would still match in stack traces.
So why call it Munge? The main reason was to discourage developers from overusing it, so I picked a word that suggested that the tool might do nasty things to your source files (plus I'm an old Zork fan). It doesn't hurt your source files, however, and can't if you use version control (which you should).
Munge was used to build Swing until the 1.1-compatible version was finally retired, and was quietly used by other JDK teams for similarly reasons. Now that the JDK team doesn't include Munge in its build any more, hopefully other developers find it useful.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Wow, it's great to read some stories about Swing's genesis. I have to say, I am proud I started using Swing when it was available for the JDK 1.1. I'm sure you had a lot of fun writing it (well, at least long nights :)
Posted by: gfx on September 05, 2006 at 03:33 PM
-
Tom, I think it's great to hear these stories, in part because sometimes there's an air of secrecy about the development (the whys and hows) of the JDK which I think creates resentment among outsiders when they don't like the resulting API. Case in point would be generics and the migration compatibility issue, which as far as I can tell only really became public after Java 5 was released, on Neil Gafter's blog and some discussion threads. I'm hoping that with the move to open-source, these sorts of discussions will be done openly and publically, so that everyone can see the more difficult decisions being wrestled with. Last, war stories around software development are always fun to listen to. I can almost feel the fire crackling and the clink of cheep beer bottles around the campfire. Cheers, Patrick
Posted by: pdoubleya on September 05, 2006 at 11:47 PM
-
I suppose it would've been politically incorrect to add a macro processor just like it would be politically incorrect to make strong typing optional in Java...
Posted by: ilazarte on September 06, 2006 at 05:11 AM
-
Hi! Here (http://www.javalobby.org/java/forums/t18116.html?start=0) we had a big discussion about preprocessor in Java. My point was, that Java should have some kind of preprocessing standard (mybe JSR) , because in real world you can't allways produce optimal code and you have to compromise with dirty tricks too. I put even examples, where preproccesing is the only good solution. Amoust everybody was against preprocessing, but nobody gave better solution for my examples. And now I heard, that the JDK teams used some kind of preprocessing too. Why? The reason is simple - sometimes is the preprocessing method best solution. I think Munge is nice way for preprocessing the Java code and I thint that it shoud be part of JDK. Gorazd Praprotnik
Posted by: gorazd_praprotn on September 06, 2006 at 05:26 AM
-
you can use cpp with Java if you want to - in C++ mode as multiline and single line comments use the same syntax - it looks like Munge's advantage was that your source files were still valid Java, maybe for IDEs to process.
preprocessing in the sense of cpp is an enormously bad thing, ... (removed personal remark)
the reason its so bad is that it makes your source code parameterized by variables that can take on unbounded values, and there is no way to determine where these variable occur in the source code (e.g. 'int' might be such a variable). Its also bad because it allows textual inclusion, so any individual includee might not be a valid syntactic structure until in the context of another and until macros have been resolved. C/C++ preprocessor is also bad because its so badly specified what the behaviour should be (i.e. informally defined) and different implementations do different things. Its also bad because function-style-macros look like language constructs but behave very differently and are a rich source of bugs.
C# has a notion of 'conditional compilation' where syntax that looks very much (indentical in fact) to some cpp syntax is /part of the language/ so that you can optionally skip some parts of your code. I'm not convinced this is necessarily good, but its much better than the text-based equivalent of C and C++ as its amenable to tools for analysis (think IDEs)
Another contender in the Java space for solutions in this area is AOP which does have good tooling available.
My opinion, is that it would be really bad to add a preprocessor to Java as its the sort of language feature that can be abused by people who don't know what they are doing. One of Java's strengths is that there is only so much damage you can do in a limited size of code. Adding a preprocessor changes this completely.
thanks,
asjf
Posted by: asjf on September 06, 2006 at 06:35 AM
-
I've never understood why Graphics, Graphics2D, and Toolkit aren't interfaces. That wouldn't have solved the package naming problem, but it would have eliminated the Graphics problem.
Posted by: coxcu on September 06, 2006 at 07:17 AM
-
Ah, because Josh Bloch hadn't joined the team yet? :-) Java "best practices" are continually developed and improved over the course of years of experience by our whole community, and no one had the experience back then we now all rely upon. Re-read James Gosling's preface to Effective Java, where he says that he wished this book had been written years ago. Wisdom generally grows by learning from mistakes we later regret.
Posted by: tball on September 06, 2006 at 08:16 AM
|