Skip to main content

Munge: Swing's Secret Preprocessor

Posted by tball on September 5, 2006 at 1:57 PM PDT

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.

Related Topics >>