If you are a programming languages enthusiast, you'll probably like this. I downloaded ANTLR Parser Generator. In addition to ANTLR, I downloaded the following:
I've been receiving many "wrong" calls these days. Apparently, there is a hospital with a telephone number starting with "2664". And my telephone number starts with "2446" and the rest of the digits are same! Looks like folks get confused and dial "44" instead of "66". I end up receiving calls to "Dr. XYZ" at odd times!! Now, I understand what doctors have to go though in their life. Our after working hour conference calls are lot better - at least our US friends understand time zone difficulties and schedule meetings around 8 AM west coast time (which is ~ 8.30 PM in India).
I downloaded java compiler (javac) source code from the JDK 7 site. I did not download entire JDK – I just downloaded compiler-7-ea-src-b15-05_jul_2007.zip I've installed JDK 6 and NetBeans 5.0
I extracted the source zip file into c:\javac directory. From NetBeans IDE, File->Open Project menu, I chose c:\javac\compiler directory. Then, I build the project – I scrolled the build output log to the end and I saw:
Building jar: C:\javac\compiler\dist\lib\javac.jar
build-bin.javac:
Copying 1 file to C:\javac\compiler\dist\bin
build:
BUILD SUCCESSFUL (total time: 8 seconds)
So, I tried to run the newly compiled java compiler. I attempted to compile a simple “Hello World” program. I got the following error:
C:\javac\compiler\dist\lib>java -jar javac.jar Hello.java
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/javac/Main
What happened? I looked at the build log again. I missed the following lines – because I had seen only at the end!!
Copying 7 files to C:\javac\compiler\build\bootclasses
recompiling compiler with itself
javac: invalid flag: C:\javac\compiler\build\classes
Usage: javac <options> <source files>
use -help for a list of possible options
Java Result: 2
Copying 7 files to C:\javac\compiler\build\classes
Copying 1 file to C:\javac\compiler\build\classes\com\sun\tools\javac\resources
Building jar: C:\javac\compiler\dist\lib\javac.jar
build-bin.javac:
Copying 1 file to C:\javac\compiler\dist\bin
build:
BUILD SUCCESSFUL (total time: 8 seconds)
Looks like there is a build error. The compiler in built in two steps:
The sources are built with javac in JDK 6 (on which my NetBeans IDE ran)
Then, compiler sources are built again – but this time with the new compiler binary generated by step (1).
Looks we got error in the step (2) [see above: recompiling compiler with itself] . I searched the ant script used to build for “recompiling compiler with itself”. The following is the fragment after that:
<echo message="recompiling compiler with itself"/>
<pathconvert pathsep=" " property="src.javac.files">
<path>
<fileset dir="${src.classes}">
<patternset refid="src.javac"/>
</fileset>
</path>
</pathconvert>
<java fork="true" classpath="${build.bootclasses}" classname="com.sun.tools.javac.Main">
<arg value="-sourcepath"/>
<arg value=""/>
<arg value="-d"/>
<arg file="${build.classes}"/>
<arg value="-g:source,lines"/>
<arg line="${src.javac.files}"/>
</java>
The problem seems to be with “java” command above. Empty string is set as value for -sourcepath option. I changed that to the following:
<arg value="-sourcepath"/>
<arg value="${src.classes}"/>
When I re-built the compiler after the above change, there were no errors – yes, I scrolled the build output to check it
And newly compiled javac could compile “Hello World” program.
Now, I wanted to make some to “interesting” but simple change to the compiler source. From a “doc” page, I came to know that there is a hidden javac option called “-printflat”. It appears that with -printflat option javac prints source code after doing transformations for generic types, inner classes, enhanced for-loops, assertions etc. It would be great to visualize the kind of transformations done by javac. So, I wanted to make “hidden” option available. I searched for “printflat” in the project. I got three hits:
JavaCompiler.java
RecognizedOptions.java
java.properties
As usual, I am impatient – wanted to enable printflat option always [regardless of what the command line is]. So, I changed the following line in JavaCompiler.java
printFlat = options.get("-printflat") != null;
to
printFlat = true; // options.get("-printflat") != null;
so that the secret option is enabled always. After rebuilding the compiler, I tried compiling my “Hello World” program. Surprise! I got the following error:
C:\javac\compiler\dist\lib>java -jar javac.jar Hello.java
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/tools/javac/Main
When I checked “javac.jar” by “jar tvf javac.jar”, I saw only “.java” files instead of “.class” files! Remember I mentioned that javac is recompiled by itself (step (2) above)? Apparently with “-printflat” option, javac just write transformed files but does not generate .class files! Because I had hardcoded printflat to be true always, during the second bootstrap compilation javac did not generate .class files. Looks like my lazy way does not work! I need to find how to really change the code to accept printflat command line option explicitly. I cut the story shot and just summarize the changes I made:
added a enum value to com.sun.tools.javac.main.OptionName – PRINTFLAT("-printflat");
In com.sun.tools.javac.main.RecognizedOptions class, I added PRINTFLAT to “static Set<OptionName> javacOptions” initialization value.
In public static Option[] getAll(final OptionHelper helper) method of RecognizedOptions class, I added “new HiddenOption(PRINTFLAT)” as an element in the returned Option[].
I managed to compile and run the compiler after the above changes! Now when I can pass “printflat” option!! I compiled the following simple Book.java:
class Book {
private String name;
public Book(String name) {
this.name = name;
}
class Order {
private int quantity;
public Order(int quantity) {
this.quantity = quantity;
}
}
}
with the following command:
c:\javac\compiler\dist\lib\>java -jar javac.jar -printflat c:\Book.java
Now, I can see the generated Book.java and Book$Order.java in the current directory where java compiler was run:
class Book {
private String name;
public Book(String name) {
super();
this.name = name;
}
{
}
}
class Book$Order {
/*synthetic*/ final Book this$0;
private int quantity;
public Book$Order(/*synthetic*/ final Book this$0, int quantity) {
this.this$0 = this$0;
super();
this.quantity = quantity;
}
}
Wow! I can see how java compiler generates a hidden synthetic parameter for the outer class object and so on. Note that the java compiler does not overwrite your original source files. You need to run the compiler in a different directory – compiler generates new files [which is good, you won't accidentally overwrite your original code with generics, inner classes and so on].
Now, you can experiment with constructs like asserts, inner class methods accessing outer's private methods/fields, anonymous/local classes, local class accessing final parameters/locals of enclosing method, generics, enhanced for-loop and so on and see how java compiler transforms those constructs to generate good-old “flat” classes without these features. Have fun!!
Jim Holmlund (blog?) compiled this very nice summary of serviceability related code in hotspot JVM -- http://openjdk.java.net/groups/serviceability/.
One of the frequently asked questions to me is this: "do you know one Mr/Mrs/Ms/Dr XYZ at Sun Microsystems?". Invariably, I don't seem to know the person mentioned
I tend to explain that Sun is a big company and it is not possible to know each and every engineer/manager/support staff and so on. And full time work-from-home does not help either - one gets even less chance to meet people
I've to wait till next JavaOne/Sun Tech Days or some other event to meet my friends.